diff --git a/index.js b/index.js index 2131b5b0..35ea662a 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,6 @@ import {addAsync, Router} from '@awaitjs/express'; import express from 'express'; import bodyParser from 'body-parser'; -import multer from 'multer'; import winston from 'winston'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc.js'; @@ -12,9 +11,11 @@ import passport from 'passport'; import session from 'express-session'; import {Writable} from 'stream'; import 'winston-daily-rotate-file'; +import formidable from 'formidable'; +import concatStream from 'concat-stream'; import { buildTrackString, - capitalize, + capitalize, createLabelledLogger, labelledFormat, longestString, readJson, sleep, @@ -24,13 +25,10 @@ import Clients from './clients/ScrobbleClients.js'; import ScrobbleSources from "./sources/ScrobbleSources.js"; import {makeClientCheckMiddle, makeSourceCheckMiddle} from "./server/middleware.js"; import TautulliSource from "./sources/TautulliSource.js"; -import PlexSource from "./sources/PlexSource.js"; +import PlexSource, {plexRequestMiddle} from "./sources/PlexSource.js"; import JellyfinSource from "./sources/JellyfinSource.js"; import { Server } from "socket.io"; -const storage = multer.memoryStorage() -const upload = multer({storage: storage}) - dayjs.extend(utc) dayjs.extend(isBetween); dayjs.extend(relativeTime); @@ -283,21 +281,27 @@ const configDir = process.env.CONFIG_DIR || `${process.cwd()}/config`; res.send('OK'); }); - app.postAsync('/plex', upload.any(), async function (req, res) { - const { - body: { - payload - } = {} - } = req; - if (payload !== undefined) { - const playObj = PlexSource.formatPlayObj(JSON.parse(payload), true); + const plexMiddle = plexRequestMiddle(); + const plexLog = createLabelledLogger('plexReq', 'Plex Request'); + app.postAsync('/plex', plexMiddle, async function (req, res) { + const { payload } = req; + if(payload === undefined) { + plexLog.warn('Received a request without any data'); + res.send('OK'); + } - const pSources = scrobbleSources.getByType('plex'); - for (const source of pSources) { - await source.handle(playObj, scrobbleClients); - } + const playObj = PlexSource.formatPlayObj(payload, true); + + const pSources = scrobbleSources.getByType('plex'); + if(pSources.length === 0) { + plexLog.warn('Received valid Plex webhook payload but no Plex sources are configured'); } + res.send('OK'); + + for (const source of pSources) { + await source.handle(playObj, scrobbleClients); + } }); // webhook plugin sends json with context type text/utf-8 so we need to parse it differently diff --git a/package-lock.json b/package-lock.json index fa01a0c0..b572f349 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,16 +56,16 @@ "color-convert": "^1.9.0" } }, - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, "async": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", @@ -143,41 +143,9 @@ } }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "bytes": { "version": "3.1.0", @@ -263,43 +231,14 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "content-disposition": { @@ -379,36 +318,13 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", "requires": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } + "asap": "^2.0.0", + "wrappy": "1" } }, "ee-first": { @@ -651,9 +567,14 @@ } }, "formidable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", - "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.4.tgz", + "integrity": "sha512-8/5nJsq+o2o1+Dryx1k5gLTDaw0dNE9kL4P3srKArO6zhoerGm42/R8zq+L5EkV7kckNTvJpJke0kI8JseL3RQ==", + "requires": { + "dezalgo": "1.0.3", + "hexoid": "1.0.0", + "once": "1.4.0" + } }, "forwarded": { "version": "0.1.2", @@ -670,6 +591,11 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -803,19 +729,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, "moment": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", @@ -826,21 +739,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "multer": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", - "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", - "requires": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - } - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -874,6 +772,14 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, "one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -1121,11 +1027,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -1150,6 +1051,13 @@ "qs": "^6.9.4", "readable-stream": "^3.6.0", "semver": "^7.3.2" + }, + "dependencies": { + "formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" + } } }, "supports-color": { @@ -1187,7 +1095,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "uid-safe": { "version": "2.1.5", @@ -1287,15 +1195,15 @@ } } }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "ws": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" } } } diff --git a/package.json b/package.json index 4e690da8..ef2535cb 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,13 @@ "@awaitjs/express": "^0.6.3", "body-parser": "^1.19.0", "compare-versions": "^4.1.2", + "concat-stream": "^2.0.0", "dayjs": "^1.10.4", "ejs": "^3.1.6", "express": "^4.17.1", "express-session": "^1.17.2", + "formidable": "^3.2.4", "lastfm-node-client": "^2.2.0", - "multer": "^1.4.2", "passport": "^0.5.0", "passport-deezer": "^0.2.0", "safe-stable-stringify": "^1.1.1", diff --git a/sources/PlexSource.js b/sources/PlexSource.js index 4f3b423d..6b9133a3 100644 --- a/sources/PlexSource.js +++ b/sources/PlexSource.js @@ -1,7 +1,9 @@ import dayjs from "dayjs";import LastFm from "lastfm-node-client"; import LastfmScrobbler from '../clients/LastfmScrobbler.js'; -import {buildTrackString} from "../utils.js"; +import {buildTrackString, createLabelledLogger} from "../utils.js"; import AbstractSource from "./AbstractSource.js"; +import formidable from 'formidable'; +import concatStream from 'concat-stream'; export default class PlexSource extends AbstractSource { users; @@ -106,7 +108,7 @@ export default class PlexSource extends AbstractSource { if (user === undefined) { this.logger.warn(`Config defined users but payload contained no user info${hint}`); } else if (!this.users.includes(user.toLocaleLowerCase())) { - this.logger.debug(`Will not scrobble event because author was not an allowed user: ${user}`, { + this.logger.verbose(`Will not scrobble event because author was not an allowed user: ${user}`, { artists, track }) @@ -115,7 +117,7 @@ export default class PlexSource extends AbstractSource { } if (event !== undefined && event !== 'media.scrobble') { - this.logger.debug(`Will not scrobble event because it is not media.scrobble (${event})`, { + this.logger.verbose(`Will not scrobble event because it is not media.scrobble (${event})`, { artists, track }) @@ -123,7 +125,7 @@ export default class PlexSource extends AbstractSource { } if (mediaType !== 'track') { - this.logger.debug(`Will not scrobble event because media type was not a track (${mediaType})`, { + this.logger.verbose(`Will not scrobble event because media type was not a track (${mediaType})`, { artists, track }); @@ -134,7 +136,7 @@ export default class PlexSource extends AbstractSource { if (library === undefined) { this.logger.warn(`Config defined libraries but payload contained no library info${hint}`); } else if (!this.libraries.includes(library.toLocaleLowerCase())) { - this.logger.debug(`Will not scrobble event because library was not on allowed list: ${library}`, { + this.logger.verbose(`Will not scrobble event because library was not on allowed list: ${library}`, { artists, track }) @@ -146,7 +148,7 @@ export default class PlexSource extends AbstractSource { if (server === undefined) { this.logger.warn(`Config defined server but payload contained no server info${hint}`); } else if (!this.servers.includes(server.toLocaleLowerCase())) { - this.logger.debug(`Will not scrobble event because server was not on allowed list: ${server}`, { + this.logger.verbose(`Will not scrobble event because server was not on allowed list: ${server}`, { artists, track }) @@ -173,3 +175,85 @@ export default class PlexSource extends AbstractSource { } } } + +export const plexRequestMiddle = () => { + + const plexLog = createLabelledLogger('plexReq', 'Plex Request'); + + return async (req, res, next) => { + + const form = formidable({ + allowEmptyFiles: true, + multiples: true, + fileWriteStreamHandler: (file) => { + return concatStream((data) => { + file.buffer = data; + }) + } + }); + form.on('progress', (received, expected) => { + plexLog.debug(`Received ${received} bytes of expected ${expected}`); + }); + form.on('error', (err) => { + plexLog.error(err); + }) + form.on('aborted', () => { + plexLog.warn('Request aborted') + }) + form.on('end', () => { + plexLog.debug('Received end of form data from Plex'); + }); + form.on('fileBegin', (formname, file) => { + plexLog.debug(`File Begin: ${formname}`); + }); + form.on('file', (formname,) => { + plexLog.debug(`File Recieved: ${formname}`); + }); + + + plexLog.debug('Receiving request from Plex...'); + + return new Promise((resolve, reject) => { + form.parse(req, (err, fields, files) => { + if (err) { + plexLog.error('Error occurred while parsing formdata'); + plexLog.error(err); + next(err); + reject(err); + return; + } + + let validFile = null; + for (const namedFiles of Object.values(files)) { + for(const file of namedFiles) { + if (file.mimetype.includes('json')) { + validFile = file; + break; + } + } + } + if (validFile === null) { + const err = new Error(`No files parsed from formdata had a mimetype that included 'json'. Found files:\n ${Object.entries(files).map(([k, v]) => `${k}: ${v.mimetype}`).join('\n')}`); + plexLog.error(err); + next(err); + reject(err); + return; + } + + const payloadRaw = validFile.buffer.toString(); + let payload = null; + try { + payload = JSON.parse(payloadRaw); + req.payload = payload; + next(); + resolve(); + } catch (e) { + plexLog.error(`Error occurred while trying to parse Plex file payload to json. Raw text:\n${payloadRaw}`); + plexLog.error(e); + next(e); + reject(e); + } + }); + }); + } +} -- 2.51.2 From 26cd478c6f6878d6ee33c292150d8c37f62312a9 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 15 Jun 2022 12:01:38 -0400 Subject: [PATCH 02/64] fix(plex): Properly handle requests without data --- index.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index 35ea662a..24e37f04 100644 --- a/index.js +++ b/index.js @@ -286,22 +286,23 @@ const configDir = process.env.CONFIG_DIR || `${process.cwd()}/config`; app.postAsync('/plex', plexMiddle, async function (req, res) { const { payload } = req; if(payload === undefined) { + plexLog.warn('Received a request without any data'); - res.send('OK'); - } - const playObj = PlexSource.formatPlayObj(payload, true); + } else { + const playObj = PlexSource.formatPlayObj(payload, true); + + const pSources = scrobbleSources.getByType('plex'); + if(pSources.length === 0) { + plexLog.warn('Received valid Plex webhook payload but no Plex sources are configured'); + } - const pSources = scrobbleSources.getByType('plex'); - if(pSources.length === 0) { - plexLog.warn('Received valid Plex webhook payload but no Plex sources are configured'); + for (const source of pSources) { + await source.handle(playObj, scrobbleClients); + } } res.send('OK'); - - for (const source of pSources) { - await source.handle(playObj, scrobbleClients); - } }); // webhook plugin sends json with context type text/utf-8 so we need to parse it differently -- 2.51.2 From c5b7dbd50f0c6cca2ee9b46a4b1a59edb848b231 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 16 Jun 2022 11:06:06 -0400 Subject: [PATCH 03/64] fix: Improve scrobble client init flow and retries * Refactor init state to have an interim value and use getter/setter to control it * Replace usage of 'ready' class field on maloja client with serverIsHealthy (make it client specific) * Add some missing functions in abstract client class * If client is not already initialized (or trying to initialize) then try to initialize client on scrobble attempt, maybe fixes #44 --- clients/AbstractScrobbleClient.js | 35 ++++++++++++++++++++++++++++--- clients/LastfmScrobbler.js | 4 +++- clients/MalojaScrobbler.js | 23 ++++++++++---------- clients/ScrobbleClients.js | 15 +++++++++---- common/index.js | 11 ++++++++++ 5 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 common/index.js diff --git a/clients/AbstractScrobbleClient.js b/clients/AbstractScrobbleClient.js index 0f90ab91..e835b996 100644 --- a/clients/AbstractScrobbleClient.js +++ b/clients/AbstractScrobbleClient.js @@ -1,11 +1,14 @@ import dayjs from "dayjs"; import {buildTrackString, capitalize, createLabelledLogger, playObjDataMatch} from "../utils.js"; +import {INITIALIZED, INITIALIZING, NOT_INITIALIZED} from "../common/index.js"; export default class AbstractScrobbleClient { name; type; - initialized = false; + + #initState = NOT_INITIALIZED; + requiresAuth = false; requiresAuthInteraction = false; authed = false; @@ -63,11 +66,28 @@ export default class AbstractScrobbleClient { }; } + get initialized() { + return this.#initState === INITIALIZED; + } + + set initialized(val) { + if(val === INITIALIZING) { + this.#initState = INITIALIZING; + } else if(val === true || val === INITIALIZED) { + this.#initState = INITIALIZED; + } else { + this.#initState = NOT_INITIALIZED; + } + } + + get initializing() { + return this.#initState === INITIALIZING; + } + // default init function, should be overridden if init stage is required initialize = async () => { this.initialized = true; - this.ready = true; - return this.initialized; + return true; } // default init function, should be overridden if auth stage is required @@ -79,6 +99,15 @@ export default class AbstractScrobbleClient { return this.initialized && (!this.requiresAuth || (this.requiresAuth && this.authed)); } + refreshScrobbles = async () => { + this.logger.debug('Scrobbler does not have refresh function implemented!'); + } + + alreadyScrobbled = async () => { + this.logger.debug('Scrobbler does not have alreadyScrobbled check implemented!'); + return false; + } + scrobblesLastCheckedAt = () => { return this.lastScrobbleCheck; } diff --git a/clients/LastfmScrobbler.js b/clients/LastfmScrobbler.js index 1175bfc2..bf4e863f 100644 --- a/clients/LastfmScrobbler.js +++ b/clients/LastfmScrobbler.js @@ -9,6 +9,7 @@ import { truncateStringToLength, } from "../utils.js"; import LastfmApiClient from "../apis/LastfmApiClient.js"; +import {INITIALIZING} from "../common/index.js"; export default class LastfmScrobbler extends AbstractScrobbleClient { @@ -24,6 +25,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { formatPlayObj = obj => LastfmApiClient.formatPlayObj(obj); initialize = async () => { + this.initialized = INITIALIZING; this.initialized = await this.api.initialize(); return this.initialized; } @@ -100,7 +102,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { return track.toLocaleLowerCase().trim(); } - alreadyScrobbled = (playObj, log = false) => { + alreadyScrobbled = async (playObj, log = false) => { return this.existingScrobble(playObj, (log || this.verboseOptions.match.onMatch)) !== undefined; } diff --git a/clients/MalojaScrobbler.js b/clients/MalojaScrobbler.js index 8bfa5345..1fb39897 100644 --- a/clients/MalojaScrobbler.js +++ b/clients/MalojaScrobbler.js @@ -11,13 +11,14 @@ import { truncateStringToLength, parseRetryAfterSecsFromObj } from "../utils.js"; +import {INITIALIZING} from "../common/index.js"; const feat = ["ft.", "ft", "feat.", "feat", "featuring", "Ft.", "Ft", "Feat.", "Feat", "Featuring"]; export default class MalojaScrobbler extends AbstractScrobbleClient { requiresAuth = true; - ready = false; + serverIsHealthy = false; serverVersion; constructor(name, config = {}, options = {}) { @@ -208,6 +209,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { initialize = async () => { // just checking that we can get a connection + this.initialized = INITIALIZING; this.initialized = await this.testConnection(); return this.initialized; } @@ -259,25 +261,24 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } isReady = async () => { - if (this.ready) { - return this.ready; + if (this.serverIsHealthy) { + return true; } try { const [isHealthy, status] = await this.testHealth(); if (!isHealthy) { this.logger.error(`Server is not ready: ${status}`); - this.ready = false; - return this.ready; + this.serverIsHealthy = false; + } else { + this.logger.info('Server reported database is built and status is healthy'); + this.serverIsHealthy = true; } - this.logger.info('Server reported database is built and status is healthy'); - this.ready = true; - return this.ready; } catch (e) { this.logger.error(`Testing server health failed due to an unexpected error`); - this.ready = false; - return this.ready; + this.serverIsHealthy = false; } + return this.serverIsHealthy } refreshScrobbles = async () => { @@ -326,7 +327,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { return lowerTitle; } - alreadyScrobbled = (playObj, log = false) => { + alreadyScrobbled = async (playObj, log = false) => { return this.existingScrobble(playObj, (log || this.verboseOptions.match.onMatch)) !== undefined; } diff --git a/clients/ScrobbleClients.js b/clients/ScrobbleClients.js index 746bfc9c..26cc3c7e 100644 --- a/clients/ScrobbleClients.js +++ b/clients/ScrobbleClients.js @@ -11,6 +11,7 @@ import LastfmScrobbler from "./LastfmScrobbler.js"; export default class ScrobbleClients { + /** @type AbstractScrobbleClient[] */ clients = []; logger; configDir; @@ -275,9 +276,15 @@ ${sources.join('\n')}`); this.logger.debug(`Client '${client.name}' was filtered out by '${scrobbleFrom}'`); continue; } - if(client.initialized === false) { - this.logger.warn(`Cannot scrobble to Client '${client.name}' because it is not yet initialized`); - continue; + if(!client.initialized) { + if(client.initializing) { + this.logger.warn(`Cannot scrobble to Client '${client.name}' because it is still initializing`); + continue; + } + if(!(await client.initialize())) { + this.logger.warn(`Cannot scrobble to Client '${client.name}' because it could not be initialized`); + continue; + } } if(client.requiresAuth && !client.authed) { @@ -310,7 +317,7 @@ ${sources.join('\n')}`); newFromSource = false, } = {} } = playObj; - if (client.timeFrameIsValid(playObj, newFromSource) && !client.alreadyScrobbled(playObj, newFromSource)) { + if (client.timeFrameIsValid(playObj, newFromSource) && !(await client.alreadyScrobbled(playObj, newFromSource))) { await client.scrobble(playObj) client.tracksScrobbled++; // since this is what we return to the source only add to tracksScrobbled if not already in array diff --git a/common/index.js b/common/index.js new file mode 100644 index 00000000..0139423c --- /dev/null +++ b/common/index.js @@ -0,0 +1,11 @@ +export const NOT_INITIALIZED = 0; +export const INITIALIZING = 1; +export const INITIALIZED = 2; + +export const initStates = [NOT_INITIALIZED, INITIALIZING, INITIALIZED]; + +export const NOT_READY = 0; +export const GETTING_READY = 1; +export const READY = 2; + +export const readyStates = [NOT_READY, GETTING_READY, READY]; -- 2.51.2 From c5a938d4ebd54e9fbd350516d129836158e8ab69 Mon Sep 17 00:00:00 2001 From: CPU_Blanc Date: Tue, 29 Nov 2022 02:01:20 +0000 Subject: [PATCH 04/64] Fix Jellyfin source --- sources/JellyfinSource.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sources/JellyfinSource.js b/sources/JellyfinSource.js index baf31297..aec916a8 100644 --- a/sources/JellyfinSource.js +++ b/sources/JellyfinSource.js @@ -45,7 +45,7 @@ export default class JellyfinSource extends MemorySource { const { ServerId, ServerName, - Username, + NotificationUsername, UserId, NotificationType, UtcTimestamp, @@ -76,7 +76,7 @@ export default class JellyfinSource extends MemorySource { event: NotificationType, mediaType: ItemType, sourceId: ItemId, - user: Username, + user: NotificationUsername, server: ServerName, source: 'Jellyfin', newFromSource, @@ -97,7 +97,7 @@ export default class JellyfinSource extends MemorySource { if (this.users !== undefined) { if (user === undefined) { - this.logger.warn(`Config defined users but payload contained no user info${hint}`); + this.logger.warn(`Config defined users but payload contained no user info`); } else if (!this.users.includes(user.toLocaleLowerCase())) { this.logger.debug(`Will not scrobble event because author was not an allowed user: ${user}`, { artists, -- 2.51.2 From 517df8a3adbd8933071a9985eb1e5922dc664837 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 3 Feb 2023 11:02:33 -0500 Subject: [PATCH 05/64] chore: remove quotes from nvmrc --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 8da27ebe..518633e1 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -"lts/fermium" +lts/fermium -- 2.51.2 From 6561ec94042e6887edab822a51dbec372842505f Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 3 Feb 2023 11:04:49 -0500 Subject: [PATCH 06/64] fix: Fix retrieving incorrect source for spotify callback when sources are unnamed MS incorrectly chooses the first unnamed source (based on built source order) when handling spotify callback. Fixed by specify source type when retrieving source. Fixes #63 --- index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 24e37f04..a4ed8b71 100644 --- a/index.js +++ b/index.js @@ -479,8 +479,11 @@ const configDir = process.env.CONFIG_DIR || `${process.cwd()}/config`; return res.send(e.message); } } else { + // TODO right now all sources requiring source interaction are covered by logic branches (deezer above and spotify here) + // but eventually should update all source callbacks to url specific URLS to avoid ambiguity... + // wish we could use state param to identify name/source but not all auth strategies and auth provides may provide access to that logger.info('Received auth code callback from Spotify', {label: 'Spotify'}); - const source = scrobbleSources.getByName(state); + const source = scrobbleSources.getByName(state, 'spotify'); const tokenResult = await source.handleAuthCodeCallback(req.query); let responseContent = 'OK'; if (tokenResult === true) { -- 2.51.2 From 0b2d4b5f897aea29d328ae977024b0a7a7bdb0a9 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 11:12:44 -0500 Subject: [PATCH 07/64] feat: Add more information to jellyfin play object and logging * Standardize runtime stamp parsing * Add playback position for future use * Add source version and log first-seen server logging to jellyfin source --- sources/JellyfinSource.js | 21 ++++++----- utils.js | 73 +++++++++++++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/sources/JellyfinSource.js b/sources/JellyfinSource.js index aec916a8..f72d7300 100644 --- a/sources/JellyfinSource.js +++ b/sources/JellyfinSource.js @@ -1,12 +1,14 @@ import MemorySource from "./MemorySource.js"; import dayjs from "dayjs"; -import {buildTrackString} from "../utils.js"; +import {buildTrackString, parseDurationFromTimestamp} from "../utils.js"; export default class JellyfinSource extends MemorySource { users; servers; + seenServers = {}; + constructor(name, config, clients, type = 'jellyfin') { super(type, name, config, clients); const {users, servers} = config @@ -45,6 +47,7 @@ export default class JellyfinSource extends MemorySource { const { ServerId, ServerName, + ServerVersion, NotificationUsername, UserId, NotificationType, @@ -55,21 +58,17 @@ export default class JellyfinSource extends MemorySource { RunTime, ItemId, ItemType, + PlaybackPosition, } = obj; - const parsedRuntime = RunTime.split(':'); - const dur = dayjs.duration({ - hours: Number.parseInt(parsedRuntime[0]), - minutes: Number.parseInt(parsedRuntime[1]), - seconds: Number.parseInt(parsedRuntime[2]) - }); + const dur = parseDurationFromTimestamp(RunTime); return { data: { artists: [Artist], album: Album, track: Name, - duration: dur.as('seconds'), + duration: dur !== undefined ? dur.as('seconds') : undefined, playDate: dayjs(), }, meta: { @@ -80,6 +79,8 @@ export default class JellyfinSource extends MemorySource { server: ServerName, source: 'Jellyfin', newFromSource, + playbackPosition: parseDurationFromTimestamp(PlaybackPosition), + sourceVersion: ServerVersion } } } @@ -139,6 +140,10 @@ export default class JellyfinSource extends MemorySource { } handle = async (playObj, allClients) => { + if(this.seenServers[playObj.meta.server] === undefined) { + this.seenServers[playObj.meta.server] = playObj.meta.sourceVersion; + this.logger.info(`Received data from server ${playObj.meta.server} (Version ${playObj.meta.sourceVersion}) for the first time.`); + } if (!this.isValidEvent(playObj)) { return; } diff --git a/utils.js b/utils.js index e6179bb3..450f36e0 100644 --- a/utils.js +++ b/utils.js @@ -94,27 +94,33 @@ export const buildTrackString = (playObj, options = {}) => { album, track, playDate - } = {} + } = {}, + meta: { + sourceId + } = {}, } = playObj; - let str = ''; + const strParts = []; + if(include.includes('sourceId') && sourceId !== undefined) { + strParts.push(`(${sourceId})`); + } if(include.includes('artist')) { - str = `${artistsFunc(artists)}`; + strParts.push(`${artistsFunc(artists)}`) } if(include.includes('track')) { - if(str !== '') { - str = `${str} - ${trackFunc(track)}`; + if(strParts.length > 0) { + strParts.push(`- ${trackFunc(track)}`); } else { - str = `${trackFunc(track)}`; + strParts.push(`${trackFunc(track)}`); } } if (include.includes('time')) { - str = `${str} @ ${timeFunc(playDate)}` + strParts.push(`@ ${timeFunc(playDate)}`); } if (include.includes('timeFromNow')) { - str = `${str} (${timeFromNow(playDate)})` + strParts.push(`(${timeFromNow(playDate)})`) } - return str; + return strParts.join(' '); } // sorts playObj formatted objects by playDate in ascending (oldest first) order @@ -213,7 +219,12 @@ export const returnDuplicateStrings = (arr) => { export const capitalize = (str) => { return str.charAt(0).toUpperCase() + str.slice(1) } - +/** + * Check if two play objects are the same by comparing non time-related data using most-to-least specific/confidence + * + * Checks sources and source ID's (unique identifiers) first then + * Checks track, album, and artists in that order + * */ export const playObjDataMatch = (a, b) => { const { data: { @@ -349,3 +360,45 @@ export const removeUndefinedKeys = (obj) => { }); return newObj; } + +export const parseDurationFromTimestamp = (timestamp) => { + if (timestamp === null || timestamp === undefined) { + return undefined; + } + if (!(typeof timestamp === 'string')) { + throw new Error('Timestamp must be a string'); + } + if (timestamp.trim() === '') { + return undefined; + } + const parsedRuntime = timestamp.split(':'); + let hours = '0', + minutes = '0', + seconds = '0', + milli = '0'; + + switch (parsedRuntime.length) { + case 3: + hours = parsedRuntime[0]; + minutes = parsedRuntime[1]; + seconds = parsedRuntime[2]; + break; + case 2: + minutes = parsedRuntime[0]; + seconds = parsedRuntime[1]; + break; + case 1: + seconds = parsedRuntime[0]; + } + const splitSec = seconds.split('.'); + if (splitSec.length > 1) { + seconds = splitSec[0]; + milli = splitSec[1]; + } + return dayjs.duration({ + hours: Number.parseInt(hours), + minutes: Number.parseInt(minutes), + seconds: Number.parseInt(seconds), + milliseconds: Number.parseInt(milli) + }); +} -- 2.51.2 From 26613f8b68b4ae1bc97faa136112a6b0e4d24283 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 11:13:03 -0500 Subject: [PATCH 08/64] docs: Update jellyfin webhook install instructions --- docs/configuration.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 02b57e8e..d0587e6e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -147,8 +147,11 @@ See [`subsonic.json.example`](../config/subsonic.json.example) Must be using Jellyfin 10.7 or greater -* Add the [Webhook Plugin](https://github.com/crobibero/jellyfin-plugin-webhook) repository to your plugins, then restart your server -* In the Webhook settings: +* In the Jellyfin desktop web UI Navigate to -> Administration -> Dashboard -> Plugins -> Catalog + * Under Notifications -> **Webhook** -> Install, then restart your server +* Navigate back to -> Administration -> Dashboard -> Plugins -> My Plugins -> Webhook + * Click "..." -> Settings +* In Webhook settings: * `Add Generic Destination` * In the new `Generic` dropdown: * Webhook Url: `http://localhost:9078/jellyfin` -- 2.51.2 From bfc3568b005a52cd4d33a7da06e9758661500b26 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 11:13:35 -0500 Subject: [PATCH 09/64] feat: Add more debug logging for memory source play object tracking --- sources/MemorySource.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/sources/MemorySource.js b/sources/MemorySource.js index f4e7b386..5100a8bf 100644 --- a/sources/MemorySource.js +++ b/sources/MemorySource.js @@ -4,7 +4,7 @@ import dayjs from "dayjs"; export default class MemorySource extends AbstractSource { /* - * MemorySource uses its own state to maintain a list of recently played tracks and determine if a track if valid. + * MemorySource uses its own state to maintain a list of recently played tracks and determine if a track is valid. * This is necessary for any source that * * doesn't have its own source of truth for "recently played" or * * that does not return "started at" and "duration" timestamps for recent plays or @@ -24,12 +24,26 @@ export default class MemorySource extends AbstractSource { }) // if no candidates exist new plays are new candidates if(this.candidateRecentlyPlayed.length === 0) { + for(const p of lockedPlays) { + this.logger.debug(`No prior candidate recent plays! Adding new locked plays: ${buildTrackString(p, {include: ['sourceId', 'artist', 'track']})}`); + } this.candidateRecentlyPlayed = lockedPlays; } else { // otherwise determine new tracks (not found in prior candidates) const newTracks = lockedPlays.filter(x => this.candidateRecentlyPlayed.every(y => !playObjDataMatch(y, x))); + if(newTracks.length > 0) { + for(const p of newTracks) { + this.logger.debug(`New play found that does not match existing candidates will be added: ${buildTrackString(p, {include: ['sourceId', 'artist', 'track']})}`); + } + } // filter prior candidates based on new recently played - this.candidateRecentlyPlayed = this.candidateRecentlyPlayed.filter(x => lockedPlays.some(y => playObjDataMatch(x, y))); + this.candidateRecentlyPlayed = this.candidateRecentlyPlayed.filter(x => { + const candidateMatchedLocked = lockedPlays.some(y => playObjDataMatch(x, y)); + if(!candidateMatchedLocked) { + this.logger.debug(`Existing candidate not found in locked plays will be removed: ${buildTrackString(x, {include: ['sourceId', 'artist', 'track']})}`); + } + return candidateMatchedLocked; + }); // and then combine still playing with new tracks this.candidateRecentlyPlayed = this.candidateRecentlyPlayed.concat(newTracks); this.candidateRecentlyPlayed.sort(sortByPlayDate); @@ -40,7 +54,7 @@ export default class MemorySource extends AbstractSource { // a prior candidate has been playing for more than 30 seconds, time to check statefuls const matchingRecent = this.statefulRecentlyPlayed.find(x => playObjDataMatch(x, candidate)); - let stPrefix = `(Stateful Play) ${buildTrackString(candidate, {include: ['artist', 'track']})}`; + let stPrefix = `(Stateful Play) ${buildTrackString(candidate, {include: ['sourceId', 'artist', 'track']})}`; if(matchingRecent === undefined) { this.logger.debug(`${stPrefix} added after being seen for 30 seconds and not matching any prior plays`); newStatefulPlays.push(candidate); -- 2.51.2 From 8499e3197d7d23c07a74f38831fe10117793a676 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 11:53:37 -0500 Subject: [PATCH 10/64] feat: Add unknown route logging --- index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/index.js b/index.js index a4ed8b71..d64d5851 100644 --- a/index.js +++ b/index.js @@ -495,6 +495,14 @@ const configDir = process.env.CONFIG_DIR || `${process.cwd()}/config`; } }); + app.useAsync(async function (req, res) { + const remote = req.connection.remoteAddress; + const proxyRemote = req.headers["x-forwarded-for"]; + const ua = req.headers["user-agent"]; + logger.debug(`Server received ${req.method} request from ${remote}${proxyRemote !== undefined ? ` (${proxyRemote})` : ''}${ua !== undefined ? ` (UA: ${ua})` : ''} to unknown route: ${req.url}`); + return res.sendStatus(404); + }); + let anyNotReady = false; for (const source of scrobbleSources.sources.filter(x => x.canPoll === true)) { await sleep(1500); // stagger polling by 1.5 seconds so that log messages for each source don't get mixed up -- 2.51.2 From 91bb0cba45f8d587ff89268c452af7b2b002873d Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 12:30:39 -0500 Subject: [PATCH 11/64] Update to note 18 and add tsconfig --- .gitignore | 2 + .nvmrc | 2 +- package-lock.json | 2625 ++++++++++++++++++++++++++++++++------------- package.json | 30 +- tsconfig.json | 22 + 5 files changed, 1944 insertions(+), 737 deletions(-) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 58e1d7ea..757bc5f3 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,5 @@ dist *.json *.txt .idea/ + +!tsconfig.json diff --git a/.nvmrc b/.nvmrc index 518633e1..a77793ec 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/fermium +lts/hydrogen diff --git a/package-lock.json b/package-lock.json index b572f349..808d53f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,467 +1,871 @@ { "name": "multi-scrobbler", - "version": "0.1.0", - "lockfileVersion": 1, + "version": "0.4.0", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@awaitjs/express": { + "packages": { + "": { + "name": "multi-scrobbler", + "version": "0.4.0", + "license": "MIT", + "dependencies": { + "@awaitjs/express": "^0.6.3", + "ajv": "^7.2.4", + "body-parser": "^1.19.0", + "compare-versions": "^4.1.2", + "concat-stream": "^2.0.0", + "dayjs": "^1.10.4", + "ejs": "^3.1.6", + "es6-error": "^4.1.1", + "express": "^4.17.1", + "express-session": "^1.17.2", + "formidable": "^3.2.4", + "lastfm-node-client": "^2.2.0", + "passport": "^0.5.0", + "passport-deezer": "^0.2.0", + "pony-cause": "^1.1.1", + "safe-stable-stringify": "^1.1.1", + "socket.io": "^4.1.3", + "spotify-web-api-node": "^5.0.2", + "superagent": "^6.1.0", + "triple-beam": "^1.3.0", + "winston": "^3.3.3", + "winston-daily-rotate-file": "^4.5.0", + "winston-duplex": "^0.1.1", + "winston-null": "^2.0.0", + "winston-transport": "^4.4.0" + }, + "devDependencies": { + "@types/express": "^4.17.13", + "@types/express-session": "^1.17.4", + "@types/node": "^18.0.0", + "@types/passport": "^1.0.7", + "@types/triple-beam": "^1.3.2", + "ts-essentials": "^9.1.2", + "ts-node": "^10.7.0", + "tsconfig-paths": "^3.13.0", + "typescript": "^4.6.4", + "typescript-json-schema": "~0.53" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=9.3.0" + } + }, + "node_modules/@awaitjs/express": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@awaitjs/express/-/express-0.6.3.tgz", - "integrity": "sha512-x7N92+Rmy/WHEaXXunV2ArVa/AqzkJlouc8pokuVF9h6DeUtbkPaEKVYKMDEFs2u5Hbw2XfwGVyLf69yr2UG5g==" + "integrity": "sha512-x7N92+Rmy/WHEaXXunV2ArVa/AqzkJlouc8pokuVF9h6DeUtbkPaEKVYKMDEFs2u5Hbw2XfwGVyLf69yr2UG5g==", + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "express": "4.x" + } }, - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", "kuler": "^2.0.0" } }, - "@types/component-emitter": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", - "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } }, - "@types/cookie": { + "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, - "@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } }, - "@types/node": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.1.tgz", - "integrity": "sha512-N87VuQi7HEeRJkhzovao/JviiqKjDKMVKxKMfUvSKw+MbkbW8R0nA3fi/MQhhlxV2fQ+2ReM+/Nt4efdrJx3zA==" + "node_modules/@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "node_modules/@types/express-session": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz", + "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==", + "dev": true, + "dependencies": { + "@types/express": "*" } }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", + "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" + }, + "node_modules/@types/passport": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.11.tgz", + "integrity": "sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" + }, + "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==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.4.tgz", + "integrity": "sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "array-flatten": { + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "asap": { + "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, - "asynckit": { + "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "base64-arraybuffer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base64id": { + "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } }, - "base64url": { + "node_modules/base64url": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - } + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "buffer-from": { + "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "color-convert": { + "node_modules/color/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { + "dependencies": { "color-name": "1.1.3" } }, - "color-name": { + "node_modules/color/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", - "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "colors": { + "node_modules/colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", + "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" } }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "compare-versions": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.2.tgz", - "integrity": "sha512-LAfbAbAgjnIwPsr2fvJLfrSyqAhK5nj/ffIg7a5aigry9RXJfNzVnOu0Egw8Z+G8LMDu1Qig2q48bpBzjyjZoQ==" + "node_modules/compare-versions": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.4.tgz", + "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==" }, - "component-emitter": { + "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "concat-stream": { + "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "requires": { + "engines": [ + "node >= 6.0" + ], + "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - }, + "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==", "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } }, - "cookie-signature": { + "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" }, - "cors": { + "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { + "dependencies": { "object-assign": "^4", "vary": "^1" + }, + "engines": { + "node": ">= 0.10" } }, - "dayjs": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz", - "integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==" + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "engines": { + "node": ">=0.4.0" + } }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" + "node_modules/dayjs": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", + "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" } }, - "delayed-stream": { + "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "dezalgo": { + "node_modules/dezalgo": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", - "requires": { + "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, - "ee-first": { + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, - "ejs": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz", - "integrity": "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==", - "requires": { - "jake": "^10.6.1" + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" } }, - "enabled": { + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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==" }, - "encodeurl": { + "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } }, - "engine.io": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.1.1.tgz", - "integrity": "sha512-aMWot7H5aC8L4/T8qMYbLdvKlZOdJTH54FxfdFunTGvhMx1BHkJOntWArsVfgAZVwAO9LC2sryPWRcEeUzCe5w==", - "requires": { + "node_modules/engine.io": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.0.tgz", + "integrity": "sha512-OgxY1c/RuCSeO/rTr8DIFXx76IzUUft86R7/P7MMbbkuzeqJoTNw2lmeD91IyGz41QYleIIjWeMJGgug043sfQ==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~4.0.0", - "ws": "~7.4.2" + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "engine.io-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", - "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", - "requires": { - "base64-arraybuffer": "0.1.4" + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" } }, - "escape-html": { + "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "etag": { + "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } + "engines": { + "node": ">= 0.10.0" } }, - "express-session": { - "version": "1.17.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", - "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", - "requires": { - "cookie": "0.4.1", + "node_modules/express-session": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", + "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "dependencies": { + "cookie": "0.4.2", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~2.0.0", @@ -470,576 +874,991 @@ "safe-buffer": "5.2.1", "uid-safe": "~2.1.5" }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "engines": { + "node": ">= 0.8.0" } }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "dependencies": { + "moment": "^2.29.1" + } }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } }, - "file-stream-rotator": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz", - "integrity": "sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ==", - "requires": { - "moment": "^2.11.2" + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "filelist": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", - "integrity": "sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==", - "requires": { - "minimatch": "^3.0.4" + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "engines": { + "node": ">= 0.8" } }, - "fn.name": { + "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==" }, - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "requires": { + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, - "formidable": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.4.tgz", - "integrity": "sha512-8/5nJsq+o2o1+Dryx1k5gLTDaw0dNE9kL4P3srKArO6zhoerGm42/R8zq+L5EkV7kckNTvJpJke0kI8JseL3RQ==", - "requires": { + "node_modules/formidable": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", + "integrity": "sha512-GRGDJTWAZ3H+umZbF2bKcqjsTov25zgon1St05ziKdiSw3kxvI+meMJrXx3ylRmuSADOpviSakBuS4yvGCGnSg==", + "dependencies": { "dezalgo": "1.0.3", "hexoid": "1.0.0", "once": "1.4.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } }, - "fresh": { + "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } }, - "hexoid": { + "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "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/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "isarray": { + "node_modules/hexoid": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "jake": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", - "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", - "requires": { - "async": "0.9.x", - "chalk": "^2.4.2", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "engines": { + "node": ">=8" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "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-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", "filelist": "^1.0.1", "minimatch": "^3.0.4" }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" - } + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" } }, - "kuler": { + "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "lastfm-node-client": { + "node_modules/lastfm-node-client": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/lastfm-node-client/-/lastfm-node-client-2.2.0.tgz", - "integrity": "sha512-nhHxrRPaNKIJnEuRov68HGjQRAFel2UWnG86ieQAklaVOdBciC6DO0vajZjzPZGqwvUAxQzDHPsp2ceDOtKW2w==" + "integrity": "sha512-nhHxrRPaNKIJnEuRov68HGjQRAFel2UWnG86ieQAklaVOdBciC6DO0vajZjzPZGqwvUAxQzDHPsp2ceDOtKW2w==", + "engines": { + "node": ">=8.10.0" + } }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", + "node_modules/logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "dependencies": { + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, - "media-typer": { + "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/logform/node_modules/safe-stable-stringify": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } }, - "merge-descriptors": { + "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "methods": { + "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "oauth": { + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" }, - "object-assign": { + "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } }, - "object-hash": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", - "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==" + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "on-headers": { + "node_modules/on-headers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { + "dependencies": { "wrappy": "1" } }, - "one-time": { + "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==", - "requires": { + "dependencies": { "fn.name": "1.x.x" } }, - "parseurl": { + "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } }, - "passport": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz", - "integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==", - "requires": { + "node_modules/passport": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.3.tgz", + "integrity": "sha512-gGc+70h4gGdBWNsR3FuV3byLDY6KBTJAIExGFXTpQaYfbbcHCBlRRKx7RBQSpqEqc5Hh2qVzRs7ssvSfOpkUEA==", + "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "passport-deezer": { + "node_modules/passport-deezer": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/passport-deezer/-/passport-deezer-0.2.0.tgz", - "integrity": "sha1-6j0HQCoDJM2byC8N2b5Nc7EZizE=", - "requires": { + "integrity": "sha512-VNNkKc4ccpAsPFTu85mBkdtUGpipJbIohfC+ZUlDP8gkJKH/bPe7myz/Va+pKWSQ1LHsEHwo5Ljw1aARp6M+cA==", + "dependencies": { "passport-oauth2": "^1.1.2" } }, - "passport-oauth2": { + "node_modules/passport-oauth2": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz", "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==", - "requires": { + "dependencies": { "base64url": "3.x.x", "oauth": "0.9.x", "passport-strategy": "1.x.x", "uid2": "0.0.x", "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "passport-strategy": { + "node_modules/passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.1.2.tgz", + "integrity": "sha512-p5kxPPwCdbf5AdXzT1bUBJomhgBlEjRBavYNr1XUpMFIE4Hnf2roueCMXudZK5tnaAu1tTmp3GPzqwJK45IHEA==", + "dev": true + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-to-regexp": { + "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, - "pause": { + "node_modules/pause": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "node_modules/pony-cause": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", + "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", + "engines": { + "node": ">=12.0.0" + } }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } }, - "random-bytes": { + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } }, - "range-parser": { + "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "readable-stream": { + "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==", - "requires": { + "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "safe-buffer": { + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "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==" + "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" + } + ] }, - "safe-stable-stringify": { + "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==" }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "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": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { + "node_modules/send/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/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "simple-swizzle": { + "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=", - "requires": { + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { "is-arrayish": "^0.3.1" } }, - "socket.io": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.3.tgz", - "integrity": "sha512-tLkaY13RcO4nIRh1K2hT5iuotfTaIQw7cVIe0FUykN3SuQi0cm7ALxuyT5/CtDswOMWUzMGTibxYNx/gU7In+Q==", - "requires": { - "@types/cookie": "^0.4.0", - "@types/cors": "^2.8.10", - "@types/node": ">=10.0.0", + "node_modules/socket.io": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.0.tgz", + "integrity": "sha512-b65bp6INPk/BMMrIgVvX12x3Q+NqlGqSlTuvKQWt0BUJ3Hyy3JangBl7fEoWZTXbOKlCqNPbQ6MbWgok/km28w==", + "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", - "debug": "~4.3.1", - "engine.io": "~5.1.1", - "socket.io-adapter": "~2.3.1", - "socket.io-parser": "~4.0.4" - } - }, - "socket.io-adapter": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.1.tgz", - "integrity": "sha512-8cVkRxI8Nt2wadkY6u60Y4rpW3ejA1rxgcK2JuyIhmF+RMNpTy1QRtkHIDUOf3B4HlQwakMsWbKftMv/71VMmw==" - }, - "socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", - "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", + "debug": "~4.3.2", + "engine.io": "~6.4.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "spotify-web-api-node": { + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/spotify-web-api-node": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/spotify-web-api-node/-/spotify-web-api-node-5.0.2.tgz", "integrity": "sha512-r82dRWU9PMimHvHEzL0DwEJrzFk+SMCVfq249SLt3I7EFez7R+jeoKQd+M1//QcnjqlXPs2am4DFsGk8/GCsrA==", - "requires": { + "dependencies": { "superagent": "^6.1.0" } }, - "stack-trace": { + "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, - "string_decoder": { + "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==", - "requires": { + "dependencies": { "safe-buffer": "~5.2.0" } }, - "superagent": { + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/superagent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", - "requires": { + "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", + "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.2", "debug": "^4.1.1", @@ -1052,158 +1871,502 @@ "readable-stream": "^3.6.0", "semver": "^7.3.2" }, + "engines": { + "node": ">= 7.0.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" + "node_modules/superagent/node_modules/formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "text-hex": { + "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==" }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } }, - "triple-beam": { + "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==" }, - "type-is": { + "node_modules/ts-essentials": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.3.0.tgz", + "integrity": "sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw==", + "dev": true, + "peerDependencies": { + "typescript": ">=4.1.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "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==", - "requires": { + "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "typedarray": { + "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, - "uid-safe": { + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typescript-json-schema": { + "version": "0.53.1", + "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.53.1.tgz", + "integrity": "sha512-Hg+RnOKUd38MOzC0rDft03a8xvwO+gCcj1F77smw2tCoZYQpFoLtrXWBGdvCX+REliko5WYel2kux17HPFqjLQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/node": "^16.9.2", + "glob": "^7.1.7", + "path-equal": "1.1.2", + "safe-stable-stringify": "^2.2.0", + "ts-node": "^10.2.1", + "typescript": "~4.6.0", + "yargs": "^17.1.1" + }, + "bin": { + "typescript-json-schema": "bin/typescript-json-schema" + } + }, + "node_modules/typescript-json-schema/node_modules/@types/node": { + "version": "16.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz", + "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", + "dev": true + }, + "node_modules/typescript-json-schema/node_modules/safe-stable-stringify": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript-json-schema/node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "requires": { + "dependencies": { "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "uid2": { + "node_modules/uid2": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" }, - "unpipe": { + "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "utils-merge": { + "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } }, - "vary": { + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { + "node_modules/winston": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", + "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==", + "dependencies": { + "@colors/colors": "1.5.0", "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", + "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.2.0", + "logform": "^2.4.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" } }, - "winston-daily-rotate-file": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.5.0.tgz", - "integrity": "sha512-/HqeWiU48dzGqcrABRlxYWVMdL6l3uKCtFSJyrqK+E2rLnSFNsgYpvwx15EgTitBLNzH69lQd/+z2ASryV2aqw==", - "requires": { - "file-stream-rotator": "^0.5.7", + "node_modules/winston-compat": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/winston-compat/-/winston-compat-0.1.5.tgz", + "integrity": "sha512-EPvPcHT604AV3Ji6d3+vX8ENKIml9VSxMRnPQ+cuK/FX6f3hvPP2hxyoeeCOCFvDrJEujalfcKWlWPvAnFyS9g==", + "dependencies": { + "cycle": "~1.0.3", + "logform": "^1.6.0", + "triple-beam": "^1.2.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston-compat/node_modules/fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + }, + "node_modules/winston-compat/node_modules/logform": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz", + "integrity": "sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==", + "dependencies": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.2.0" + } + }, + "node_modules/winston-compat/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/winston-daily-rotate-file": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz", + "integrity": "sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==", + "dependencies": { + "file-stream-rotator": "^0.6.1", "object-hash": "^2.0.1", "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "winston": "^3" + } + }, + "node_modules/winston-duplex": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/winston-duplex/-/winston-duplex-0.1.1.tgz", + "integrity": "sha512-p6Bd/wNYZnvfx9NQa6XKcEp3IidMuKrn5P+jxhxhW+fG+tqN/fk8lI8hjhLdrLi3LCBfrmh4au910PQ1D1WOOw==", + "dependencies": { + "triple-beam": "^1.3.0", + "winston": "^3", + "winston-transport": "^4.4.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/winston-null": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/winston-null/-/winston-null-2.0.0.tgz", + "integrity": "sha512-uS5tJB5OkLWOoc3I7/LsWUfTIa5Du38XSviHf/b0TINK659Np9368FJwTt15UoZQYUQVxLpM06lxk2dKET22Xw==", + "dependencies": { + "semver": "^5.6.0", + "winston-compat": "^0.1.4", "winston-transport": "^4.2.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "winston": ">=2.0.0" } }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" + "node_modules/winston-null/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston/node_modules/safe-stable-stringify": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } } } } diff --git a/package.json b/package.json index ef2535cb..9cb2b1b9 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,16 @@ { "name": "multi-scrobbler", - "version": "0.1.0", + "version": "0.4.0", "description": "scrobble plays from multiple sources to multiple clients", "type": "module", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc" }, "engines": { - "node": ">=14.0.0", - "npm": ">=6.0.0" + "node": ">=18.0.0", + "npm": ">=9.3.0" }, "repository": { "type": "git", @@ -28,17 +29,36 @@ "concat-stream": "^2.0.0", "dayjs": "^1.10.4", "ejs": "^3.1.6", + "ajv": "^7.2.4", + "es6-error": "^4.1.1", "express": "^4.17.1", "express-session": "^1.17.2", "formidable": "^3.2.4", "lastfm-node-client": "^2.2.0", "passport": "^0.5.0", "passport-deezer": "^0.2.0", + "pony-cause": "^1.1.1", "safe-stable-stringify": "^1.1.1", "socket.io": "^4.1.3", "spotify-web-api-node": "^5.0.2", "superagent": "^6.1.0", + "triple-beam": "^1.3.0", "winston": "^3.3.3", - "winston-daily-rotate-file": "^4.5.0" + "winston-daily-rotate-file": "^4.5.0", + "winston-duplex": "^0.1.1", + "winston-null": "^2.0.0", + "winston-transport": "^4.4.0" + }, + "devDependencies": { + "@types/express": "^4.17.13", + "@types/express-session": "^1.17.4", + "@types/node": "^18.0.0", + "@types/passport": "^1.0.7", + "@types/triple-beam": "^1.3.2", + "ts-essentials": "^9.1.2", + "ts-node": "^10.7.0", + "tsconfig-paths": "^3.13.0", + "typescript": "^4.6.4", + "typescript-json-schema": "~0.53" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..c23b1bad --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "@tsconfig/node18/tsconfig.json", + "compilerOptions": { + "sourceMap": true, + "resolveJsonModule": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "typeRoots": [ + "./node_modules/@types", + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules", + "coverage", + "tests/*.ts", + "_site" + ] +} -- 2.51.2 From abf338f71a1da7fa77dda83ad6c8b4474a1b110a Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 12:46:46 -0500 Subject: [PATCH 12/64] refactor: Move application files into src folder * In preparation for documentation site and building from TS isolate app files to own folder * Fix log/config/views folder default location --- package.json | 2 +- {apis => src/apis}/AbstractApiClient.js | 0 {apis => src/apis}/LastfmApiClient.js | 0 {clients => src/clients}/AbstractScrobbleClient.js | 0 {clients => src/clients}/LastfmScrobbler.js | 0 {clients => src/clients}/MalojaScrobbler.js | 0 {clients => src/clients}/ScrobbleClients.js | 0 {common => src/common}/index.js | 7 +++++++ index.js => src/index.js | 8 +++++--- {server => src/server}/middleware.js | 0 {sources => src/sources}/AbstractSource.js | 0 {sources => src/sources}/DeezerSource.js | 0 {sources => src/sources}/JellyfinSource.js | 0 {sources => src/sources}/LastfmSource.js | 0 {sources => src/sources}/MemorySource.js | 0 {sources => src/sources}/PlexSource.js | 0 {sources => src/sources}/ScrobbleSources.js | 0 {sources => src/sources}/SpotifySource.js | 0 {sources => src/sources}/SubsonicSource.js | 0 {sources => src/sources}/TautulliSource.js | 0 utils.js => src/utils.js | 0 {views => src/views}/recent.ejs | 0 {views => src/views}/status.ejs | 0 23 files changed, 13 insertions(+), 4 deletions(-) rename {apis => src/apis}/AbstractApiClient.js (100%) rename {apis => src/apis}/LastfmApiClient.js (100%) rename {clients => src/clients}/AbstractScrobbleClient.js (100%) rename {clients => src/clients}/LastfmScrobbler.js (100%) rename {clients => src/clients}/MalojaScrobbler.js (100%) rename {clients => src/clients}/ScrobbleClients.js (100%) rename {common => src/common}/index.js (58%) rename index.js => src/index.js (98%) rename {server => src/server}/middleware.js (100%) rename {sources => src/sources}/AbstractSource.js (100%) rename {sources => src/sources}/DeezerSource.js (100%) rename {sources => src/sources}/JellyfinSource.js (100%) rename {sources => src/sources}/LastfmSource.js (100%) rename {sources => src/sources}/MemorySource.js (100%) rename {sources => src/sources}/PlexSource.js (100%) rename {sources => src/sources}/ScrobbleSources.js (100%) rename {sources => src/sources}/SpotifySource.js (100%) rename {sources => src/sources}/SubsonicSource.js (100%) rename {sources => src/sources}/TautulliSource.js (100%) rename utils.js => src/utils.js (100%) rename {views => src/views}/recent.ejs (100%) rename {views => src/views}/status.ejs (100%) diff --git a/package.json b/package.json index 9cb2b1b9..ff2e2fbf 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.4.0", "description": "scrobble plays from multiple sources to multiple clients", "type": "module", - "main": "index.js", + "main": "src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "tsc" diff --git a/apis/AbstractApiClient.js b/src/apis/AbstractApiClient.js similarity index 100% rename from apis/AbstractApiClient.js rename to src/apis/AbstractApiClient.js diff --git a/apis/LastfmApiClient.js b/src/apis/LastfmApiClient.js similarity index 100% rename from apis/LastfmApiClient.js rename to src/apis/LastfmApiClient.js diff --git a/clients/AbstractScrobbleClient.js b/src/clients/AbstractScrobbleClient.js similarity index 100% rename from clients/AbstractScrobbleClient.js rename to src/clients/AbstractScrobbleClient.js diff --git a/clients/LastfmScrobbler.js b/src/clients/LastfmScrobbler.js similarity index 100% rename from clients/LastfmScrobbler.js rename to src/clients/LastfmScrobbler.js diff --git a/clients/MalojaScrobbler.js b/src/clients/MalojaScrobbler.js similarity index 100% rename from clients/MalojaScrobbler.js rename to src/clients/MalojaScrobbler.js diff --git a/clients/ScrobbleClients.js b/src/clients/ScrobbleClients.js similarity index 100% rename from clients/ScrobbleClients.js rename to src/clients/ScrobbleClients.js diff --git a/common/index.js b/src/common/index.js similarity index 58% rename from common/index.js rename to src/common/index.js index 0139423c..bce402bd 100644 --- a/common/index.js +++ b/src/common/index.js @@ -1,3 +1,5 @@ +import path from 'path'; +import { fileURLToPath } from 'url'; export const NOT_INITIALIZED = 0; export const INITIALIZING = 1; export const INITIALIZED = 2; @@ -9,3 +11,8 @@ export const GETTING_READY = 1; export const READY = 2; export const readyStates = [NOT_READY, GETTING_READY, READY]; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export const projectDir = path.resolve(__dirname, '../../'); diff --git a/index.js b/src/index.js similarity index 98% rename from index.js rename to src/index.js index d64d5851..05d0bc7a 100644 --- a/index.js +++ b/src/index.js @@ -28,6 +28,8 @@ import TautulliSource from "./sources/TautulliSource.js"; import PlexSource, {plexRequestMiddle} from "./sources/PlexSource.js"; import JellyfinSource from "./sources/JellyfinSource.js"; import { Server } from "socket.io"; +import path from "path"; +import {projectDir} from "./common/index.js"; dayjs.extend(utc) dayjs.extend(isBetween); @@ -74,7 +76,7 @@ const logConfig = { } const availableLevels = ['info', 'debug']; -const logPath = process.env.LOG_DIR || `${process.cwd()}/logs`; +const logPath = process.env.LOG_DIR || path.resolve(projectDir, `./logs`); const localUrl = `http://localhost:${port}`; const rotateTransport = new winston.transports.DailyRotateFile({ @@ -107,7 +109,7 @@ winston.loggers.add('default', loggerOptions); const logger = winston.loggers.get('default'); -const configDir = process.env.CONFIG_DIR || `${process.cwd()}/config`; +const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`); (async function () { try { @@ -531,7 +533,7 @@ const configDir = process.env.CONFIG_DIR || `${process.cwd()}/config`; logger.info(`Some sources are not ready, open ${localUrl} to continue`); } - app.set('views', './views'); + app.set('views', path.resolve(projectDir, 'src/views')); app.set('view engine', 'ejs'); logger.info(`Server started at ${localUrl}`); diff --git a/server/middleware.js b/src/server/middleware.js similarity index 100% rename from server/middleware.js rename to src/server/middleware.js diff --git a/sources/AbstractSource.js b/src/sources/AbstractSource.js similarity index 100% rename from sources/AbstractSource.js rename to src/sources/AbstractSource.js diff --git a/sources/DeezerSource.js b/src/sources/DeezerSource.js similarity index 100% rename from sources/DeezerSource.js rename to src/sources/DeezerSource.js diff --git a/sources/JellyfinSource.js b/src/sources/JellyfinSource.js similarity index 100% rename from sources/JellyfinSource.js rename to src/sources/JellyfinSource.js diff --git a/sources/LastfmSource.js b/src/sources/LastfmSource.js similarity index 100% rename from sources/LastfmSource.js rename to src/sources/LastfmSource.js diff --git a/sources/MemorySource.js b/src/sources/MemorySource.js similarity index 100% rename from sources/MemorySource.js rename to src/sources/MemorySource.js diff --git a/sources/PlexSource.js b/src/sources/PlexSource.js similarity index 100% rename from sources/PlexSource.js rename to src/sources/PlexSource.js diff --git a/sources/ScrobbleSources.js b/src/sources/ScrobbleSources.js similarity index 100% rename from sources/ScrobbleSources.js rename to src/sources/ScrobbleSources.js diff --git a/sources/SpotifySource.js b/src/sources/SpotifySource.js similarity index 100% rename from sources/SpotifySource.js rename to src/sources/SpotifySource.js diff --git a/sources/SubsonicSource.js b/src/sources/SubsonicSource.js similarity index 100% rename from sources/SubsonicSource.js rename to src/sources/SubsonicSource.js diff --git a/sources/TautulliSource.js b/src/sources/TautulliSource.js similarity index 100% rename from sources/TautulliSource.js rename to src/sources/TautulliSource.js diff --git a/utils.js b/src/utils.js similarity index 100% rename from utils.js rename to src/utils.js diff --git a/views/recent.ejs b/src/views/recent.ejs similarity index 100% rename from views/recent.ejs rename to src/views/recent.ejs diff --git a/views/status.ejs b/src/views/status.ejs similarity index 100% rename from views/status.ejs rename to src/views/status.ejs -- 2.51.2 From cd86acdcf0b7f2568077dff7849e94bdc6080d32 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 12:53:13 -0500 Subject: [PATCH 13/64] [ts-migrate][.] Init tsconfig.json file Co-authored-by: ts-migrate <> --- .eslintrc | 0 config/config.json.bak | 31 +++++++++++++++++++++++++++++++ package.json | 1 - 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .eslintrc create mode 100644 config/config.json.bak diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..e69de29b diff --git a/config/config.json.bak b/config/config.json.bak new file mode 100644 index 00000000..54ab4c78 --- /dev/null +++ b/config/config.json.bak @@ -0,0 +1,31 @@ +{ + "sourceDefaults": { + "maxPollRetries": 0, + "maxRequestRetries": 1, + "retryMultiplier": 1.5 + }, + "clientDefaults": { + "maxRequestRetries": 1, + "retryMultiplier": 1.5 + }, + "sources": [ + { + "type": "tautulli", + "name": "myTautulli", + "clients": ["myLFM"], + "data": { + "user": ["myName"] + } + } + ], + "clients": [ + { + "type": "lastfm", + "name": "myLFM", + "data": { + "apiKey": "xxxxxxxxxxx", + "secret": "xxxxxxxxxxx" + } + } + ] +} diff --git a/package.json b/package.json index ff2e2fbf..3f5975ab 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,6 @@ "name": "multi-scrobbler", "version": "0.4.0", "description": "scrobble plays from multiple sources to multiple clients", - "type": "module", "main": "src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", -- 2.51.2 From 900f3c24713de5d1cd35f37f3d0316e62c636a6f Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 12:53:42 -0500 Subject: [PATCH 14/64] [ts-migrate][.] Init tsconfig.json file Co-authored-by: ts-migrate <> --- package-lock.json | 8484 ++++++++++++++++++++++++++++++++++++++------- package.json | 3 +- 2 files changed, 7237 insertions(+), 1250 deletions(-) diff --git a/package-lock.json b/package-lock.json index 808d53f0..5cfef882 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "@types/passport": "^1.0.7", "@types/triple-beam": "^1.3.2", "ts-essentials": "^9.1.2", + "ts-migrate": "^0.1.35", "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", "typescript": "^4.6.4", @@ -52,6 +53,19 @@ "npm": ">=9.3.0" } }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@awaitjs/express": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@awaitjs/express/-/express-0.6.3.tgz", @@ -63,1649 +77,6778 @@ "express": "4.x" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, "engines": { - "node": ">=0.1.90" + "node": ">=6.9.0" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@babel/compat-data": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" }, "engines": { - "node": ">=12" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "bin": { + "json5": "lib/cli.js" + }, "engines": { - "node": ">=6.0.0" + "node": ">=6" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "node_modules/@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", "dev": true, "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "dependencies": { - "@types/node": "*" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, - "node_modules/@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, "dependencies": { - "@types/node": "*" + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", "dev": true, "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.33", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", - "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "dev": true, "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@types/express-session": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz", - "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==", + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "@types/express": "*" + "yallist": "^3.0.2" } }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", - "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" - }, - "node_modules/@types/passport": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.11.tgz", - "integrity": "sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, - "dependencies": { - "@types/express": "*" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", + "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", "dev": true, "dependencies": { - "@types/mime": "*", - "@types/node": "*" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@types/triple-beam": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", - "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" - }, - "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==", + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", + "dev": true, "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.2.1" }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" }, - "engines": { - "node": ">=0.4.0" + "peerDependencies": { + "@babel/core": "^7.4.0-0" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, + "dependencies": { + "ms": "2.1.2" + }, "engines": { - "node": ">=0.4.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/ajv": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.4.tgz", - "integrity": "sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A==", + "node_modules/@babel/helper-define-polyfill-provider/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "@babel/types": "^7.18.6" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@babel/types": "^7.18.6" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6.9.0" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", + "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } }, - "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==" + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + "node_modules/@babel/helper-module-transforms": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "node_modules/@babel/helper-replace-supers": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" + }, "engines": { - "node": "^4.5.0 || >= 5.9" + "node": ">=6.9.0" } }, - "node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dev": true, "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "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.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "@babel/types": "^7.20.0" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=6.9.0" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@babel/helper-wrap-function": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=6.9.0" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/@babel/helpers": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.13", + "@babel/types": "^7.20.7" }, "engines": { - "node": ">=12" + "node": ">=6.9.0" } }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=4" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/color/node_modules/color-convert": { + "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } }, - "node_modules/color/node_modules/color-name": { + "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { - "node": ">=0.1.90" + "node": ">=0.8.0" } }, - "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/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { - "delayed-stream": "~1.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=4" } }, - "node_modules/compare-versions": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.4.tgz", - "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==" - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" + "node_modules/@babel/parser": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "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==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, "dependencies": { - "safe-buffer": "5.2.1" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" } }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, "dependencies": { - "object-assign": "^4", - "vary": "^1" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">= 0.10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz", + "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, "engines": { - "node": ">=0.4.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" } }, - "node_modules/dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" - }, - "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/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, "dependencies": { - "ms": "2.0.0" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, "engines": { - "node": ">=0.4.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, "engines": { - "node": ">=0.3.1" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "dev": true, "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "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", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/engine.io": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.0.tgz", - "integrity": "sha512-OgxY1c/RuCSeO/rTr8DIFXx76IzUUft86R7/P7MMbbkuzeqJoTNw2lmeD91IyGz41QYleIIjWeMJGgug043sfQ==", + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz", + "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==", + "dev": true, "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { - "node": ">=10.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, "engines": { - "node": ">=10.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, "dependencies": { - "ms": "2.1.2" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">=6.0" + "node": ">=4" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, "engines": { - "node": ">=6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "@babel/helper-plugin-utils": "^7.8.3" }, - "engines": { - "node": ">= 0.10.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/express-session": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", - "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "dev": true, "dependencies": { - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">= 0.8.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/express-session/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "node_modules/file-stream-rotator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", - "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, "dependencies": { - "moment": "^2.29.1" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">= 0.8" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "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/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">= 6" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/formidable": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", - "integrity": "sha512-GRGDJTWAZ3H+umZbF2bKcqjsTov25zgon1St05ziKdiSw3kxvI+meMJrXx3ylRmuSADOpviSakBuS4yvGCGnSg==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, "dependencies": { - "dezalgo": "1.0.3", - "hexoid": "1.0.0", - "once": "1.4.0" + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "@babel/helper-plugin-utils": "^7.20.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" }, "engines": { - "node": "*" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">= 0.4.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz", + "integrity": "sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/@babel/plugin-transform-classes": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz", + "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", + "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", + "dev": true, "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "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==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "@babel/helper-plugin-utils": "^7.18.9" + }, "engines": { - "node": ">= 0.10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "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-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-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==", + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", + "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-flow": "^7.18.6" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", "dev": true, "dependencies": { - "minimist": "^1.2.0" + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/lastfm-node-client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/lastfm-node-client/-/lastfm-node-client-2.2.0.tgz", - "integrity": "sha512-nhHxrRPaNKIJnEuRov68HGjQRAFel2UWnG86ieQAklaVOdBciC6DO0vajZjzPZGqwvUAxQzDHPsp2ceDOtKW2w==", "engines": { - "node": ">=8.10.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/logform": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", - "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, "dependencies": { - "@colors/colors": "1.5.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-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/logform/node_modules/safe-stable-stringify": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", - "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, "engines": { - "node": ">=10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "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==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", + "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" }, "engines": { - "node": "*" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, "engines": { - "node": "*" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "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==", + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, "dependencies": { - "ee-first": "1.1.1" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "node_modules/@babel/plugin-transform-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, "dependencies": { - "wrappy": "1" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "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==", + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "@babel/helper-plugin-utils": "^7.18.9" + }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/passport": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.3.tgz", - "integrity": "sha512-gGc+70h4gGdBWNsR3FuV3byLDY6KBTJAIExGFXTpQaYfbbcHCBlRRKx7RBQSpqEqc5Hh2qVzRs7ssvSfOpkUEA==", + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { - "node": ">= 0.4.0" + "node": ">=6.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/passport-deezer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/passport-deezer/-/passport-deezer-0.2.0.tgz", - "integrity": "sha512-VNNkKc4ccpAsPFTu85mBkdtUGpipJbIohfC+ZUlDP8gkJKH/bPe7myz/Va+pKWSQ1LHsEHwo5Ljw1aARp6M+cA==", + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.13.tgz", + "integrity": "sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==", + "dev": true, "dependencies": { - "passport-oauth2": "^1.1.2" + "@babel/helper-create-class-features-plugin": "^7.20.12", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/passport-oauth2": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz", - "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==", + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, "dependencies": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { - "node": ">= 0.4.0" + "node": ">=6.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, "engines": { - "node": ">= 0.4.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/path-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.1.2.tgz", - "integrity": "sha512-p5kxPPwCdbf5AdXzT1bUBJomhgBlEjRBavYNr1XUpMFIE4Hnf2roueCMXudZK5tnaAu1tTmp3GPzqwJK45IHEA==", - "dev": true - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/@babel/preset-env": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, - "node_modules/pony-cause": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", - "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", - "engines": { - "node": ">=12.0.0" + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/@babel/preset-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.18.6.tgz", + "integrity": "sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ==", + "dev": true, "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-flow-strip-types": "^7.18.6" }, "engines": { - "node": ">= 0.10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "dev": true, "dependencies": { - "side-channel": "^1.0.4" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" }, "engines": { - "node": ">=0.6" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "node_modules/@babel/register": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.18.9.tgz", + "integrity": "sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.5", + "source-map-support": "^0.5.16" + }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/@babel/runtime": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" } }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" } }, - "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==", + "node_modules/@babel/traverse": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" }, "engines": { - "node": ">= 6" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@ts-morph/bootstrap": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@ts-morph/bootstrap/-/bootstrap-0.16.0.tgz", + "integrity": "sha512-FYW3bK5EBeAgpHu0qZ57gHbLjzgzC81y5EJmrebzIhXSYg6OgZu5lFHpF5NJ7CwM7ZMhxX1PG+DRA8e+skopKw==", + "dev": true, + "dependencies": { + "@ts-morph/common": "~0.16.0" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.16.0.tgz", + "integrity": "sha512-SgJpzkTgZKLKqQniCjLaE3c2L2sdL7UShvmTmPBejAKd2OKV/yfMpQ2IWpAuA+VY5wy7PkSUaEObIqEK6afFuw==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "minimatch": "^5.1.0", + "mkdirp": "^1.0.4", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/express-session": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz", + "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", + "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" + }, + "node_modules/@types/passport": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.11.tgz", + "integrity": "sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" + }, + "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==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.4.tgz", + "integrity": "sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==" + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-types": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.7.tgz", + "integrity": "sha512-2mP3TwtkY/aTv5X3ZsMpNAbOnyoC/aMJwJSoaELPkHId0nSQgFcnU4dRW3isxiz7+zBexk0ym3WNVjMiQBnJSw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/babel-core": { + "version": "7.0.0-bridge.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", + "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "dev": true, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "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.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001451", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz", + "integrity": "sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compare-versions": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.4.tgz", + "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==" + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "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==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", + "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-jest-runner": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/create-jest-runner/-/create-jest-runner-0.5.3.tgz", + "integrity": "sha512-a9VY2doMBmzRollJB3Ft3/Y5fBceSWJ4gdyVsg4/d7nP1S4715VG939s2VnITDj79YBmRgKhjGjNRv1c+Kre1g==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "jest-worker": "^24.0.0", + "throat": "^4.1.0" + }, + "bin": { + "create-jest-runner": "generator/index.js" + } + }, + "node_modules/create-jest-runner/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/create-jest-runner/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/create-jest-runner/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/create-jest-runner/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/create-jest-runner/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/create-jest-runner/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/create-jest-runner/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", + "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "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==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.289", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.289.tgz", + "integrity": "sha512-relLdMfPBxqGCxy7Gyfm1HcbRPcFUJdlgnCPVgQ23sr1TvUrRJz0/QPoGP0+x41wOVSTN/Wi3w6YDgHiHJGOzg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.0.tgz", + "integrity": "sha512-OgxY1c/RuCSeO/rTr8DIFXx76IzUUft86R7/P7MMbbkuzeqJoTNw2lmeD91IyGz41QYleIIjWeMJGgug043sfQ==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-session": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", + "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "dependencies": { + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/fast-glob/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/fast-glob/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "dependencies": { + "moment": "^2.29.1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/flow-parser": { + "version": "0.199.1", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.199.1.tgz", + "integrity": "sha512-Mt+GFUQYij3miM7Z6o8E3aHTGXZKSOhvlCFgdQRoi6fkWfhyijnoX51zpOxM5PmZuiV6gallWhDZzwOsWxRutg==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "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/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", + "integrity": "sha512-GRGDJTWAZ3H+umZbF2bKcqjsTov25zgon1St05ziKdiSw3kxvI+meMJrXx3ylRmuSADOpviSakBuS4yvGCGnSg==", + "dependencies": { + "dezalgo": "1.0.3", + "hexoid": "1.0.0", + "once": "1.4.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "engines": { + "node": ">=8" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.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/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "dependencies": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jscodeshift": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.6.4.tgz", + "integrity": "sha512-+NF/tlNbc2WEhXUuc4WEJLsJumF84tnaMUZW2hyJw3jThKKRvsPX4sPJVgO1lPE28z0gNL+gwniLG9d8mYvQCQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.6", + "@babel/parser": "^7.1.6", + "@babel/plugin-proposal-class-properties": "^7.1.0", + "@babel/plugin-proposal-object-rest-spread": "^7.0.0", + "@babel/preset-env": "^7.1.6", + "@babel/preset-flow": "^7.0.0", + "@babel/preset-typescript": "^7.1.0", + "@babel/register": "^7.0.0", + "babel-core": "^7.0.0-bridge.0", + "colors": "^1.1.2", + "flow-parser": "0.*", + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "neo-async": "^2.5.0", + "node-dir": "^0.1.17", + "recast": "^0.16.1", + "temp": "^0.8.1", + "write-file-atomic": "^2.3.0" + }, + "bin": { + "jscodeshift": "bin/jscodeshift.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/json5-writer": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/json5-writer/-/json5-writer-0.1.8.tgz", + "integrity": "sha512-h5sqkk/vSKvESOUTBniGWs8p8nTzHsoDrxPS9enJfQVINqXv3lm+FAyizLwbrCwCn0q7NXqDBb+r8AdUdK3XZw==", + "dev": true, + "dependencies": { + "jscodeshift": "^0.6.3" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/lastfm-node-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/lastfm-node-client/-/lastfm-node-client-2.2.0.tgz", + "integrity": "sha512-nhHxrRPaNKIJnEuRov68HGjQRAFel2UWnG86ieQAklaVOdBciC6DO0vajZjzPZGqwvUAxQzDHPsp2ceDOtKW2w==", + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/log-update": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-3.4.0.tgz", + "integrity": "sha512-ILKe88NeMt4gmDvk/eb615U/IVn7K9KWGkoYbdatQ69Z65nj1ZzjM6fHXfcs0Uge+e+EGnMW7DY4T9yko8vWFg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^3.2.0", + "cli-cursor": "^2.1.0", + "wrap-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/log-update/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "dependencies": { + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "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/logform/node_modules/safe-stable-stringify": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-dir": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", + "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.2" + }, + "engines": { + "node": ">= 0.10.5" + } + }, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "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/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/passport": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.3.tgz", + "integrity": "sha512-gGc+70h4gGdBWNsR3FuV3byLDY6KBTJAIExGFXTpQaYfbbcHCBlRRKx7RBQSpqEqc5Hh2qVzRs7ssvSfOpkUEA==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-deezer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/passport-deezer/-/passport-deezer-0.2.0.tgz", + "integrity": "sha512-VNNkKc4ccpAsPFTu85mBkdtUGpipJbIohfC+ZUlDP8gkJKH/bPe7myz/Va+pKWSQ1LHsEHwo5Ljw1aARp6M+cA==", + "dependencies": { + "passport-oauth2": "^1.1.2" + } + }, + "node_modules/passport-oauth2": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz", + "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.1.2.tgz", + "integrity": "sha512-p5kxPPwCdbf5AdXzT1bUBJomhgBlEjRBavYNr1XUpMFIE4Hnf2roueCMXudZK5tnaAu1tTmp3GPzqwJK45IHEA==", + "dev": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pony-cause": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", + "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "dev": true, + "dependencies": { + "parse-ms": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "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/recast": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.16.2.tgz", + "integrity": "sha512-O/7qXi51DPjRVdbrpNzoBQH5dnAPQNbfoOFyRiUwreTMJfIHYOEBzwuH+c0+/BTSJ3CQyKs6ILSWXhESH6Op3A==", + "dev": true, + "dependencies": { + "ast-types": "0.11.7", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" } }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "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/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "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", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/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/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, "engines": { "node": ">=0.10.0" } }, - "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/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", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "is-buffer": "^1.1.5" }, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "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" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/send/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/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "is-buffer": "^1.1.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "dependencies": { - "is-arrayish": "^0.3.1" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/socket.io": { @@ -1732,74 +6875,228 @@ "ws": "~8.11.0" } }, - "node_modules/socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "node_modules/socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spotify-web-api-node": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/spotify-web-api-node/-/spotify-web-api-node-5.0.2.tgz", + "integrity": "sha512-r82dRWU9PMimHvHEzL0DwEJrzFk+SMCVfq249SLt3I7EFez7R+jeoKQd+M1//QcnjqlXPs2am4DFsGk8/GCsrA==", + "dependencies": { + "superagent": "^6.1.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "kind-of": "^3.0.2" }, "engines": { - "node": ">=10.0.0" + "node": ">=0.10.0" } }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "ms": "2.1.2" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, "dependencies": { - "ms": "2.1.2" + "kind-of": "^3.0.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/spotify-web-api-node": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/spotify-web-api-node/-/spotify-web-api-node-5.0.2.tgz", - "integrity": "sha512-r82dRWU9PMimHvHEzL0DwEJrzFk+SMCVfq249SLt3I7EFez7R+jeoKQd+M1//QcnjqlXPs2am4DFsGk8/GCsrA==", + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "dependencies": { - "superagent": "^6.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, "engines": { - "node": "*" + "node": ">=0.10.0" } }, "node_modules/statuses": { @@ -1853,6 +7150,18 @@ "node": ">=4" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/superagent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", @@ -1900,58 +7209,422 @@ "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/temp": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz", + "integrity": "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==", + "dev": true, + "dependencies": { + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "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/ts-essentials": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.3.0.tgz", + "integrity": "sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw==", + "dev": true, + "peerDependencies": { + "typescript": ">=4.1.0" + } + }, + "node_modules/ts-migrate": { + "version": "0.1.35", + "resolved": "https://registry.npmjs.org/ts-migrate/-/ts-migrate-0.1.35.tgz", + "integrity": "sha512-EmXahqwIP0a6fE1BLKKVMgJEeYn9V+CxJ19qFJ/DzkPL4PjDI/FcgPo8D519amBPy2nnlc/x1V6R6aIeHdD87w==", + "dev": true, + "dependencies": { + "create-jest-runner": "^0.5.3", + "json5": "^2.1.1", + "json5-writer": "^0.1.8", + "ts-migrate-plugins": "^0.1.35", + "ts-migrate-server": "^0.1.33", + "updatable-log": "^0.2.0", + "yargs": "^15.0.2" + }, + "bin": { + "ts-migrate": "build/cli.js", + "ts-migrate-full": "bin/ts-migrate-full.sh" + }, + "peerDependencies": { + "typescript": ">4.0" + } + }, + "node_modules/ts-migrate-plugins": { + "version": "0.1.35", + "resolved": "https://registry.npmjs.org/ts-migrate-plugins/-/ts-migrate-plugins-0.1.35.tgz", + "integrity": "sha512-DUkx7ClKhxKYPWDha9DJTZ6LhwEUszL90uH5I/O11K/6TbA96ytln1O3HL6Pt83i4mAKOlg0mQ6AMsPtL5FFkQ==", + "dev": true, + "dependencies": { + "eslint": "^7.14.0", + "jscodeshift": "^0.13.0", + "json-schema": "^0.4.0", + "ts-migrate-server": "^0.1.33" + }, + "peerDependencies": { + "typescript": ">4.0" + } + }, + "node_modules/ts-migrate-plugins/node_modules/ast-types": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-migrate-plugins/node_modules/jscodeshift": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.13.1.tgz", + "integrity": "sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.13.16", + "@babel/parser": "^7.13.16", + "@babel/plugin-proposal-class-properties": "^7.13.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", + "@babel/plugin-proposal-optional-chaining": "^7.13.12", + "@babel/plugin-transform-modules-commonjs": "^7.13.8", + "@babel/preset-flow": "^7.13.13", + "@babel/preset-typescript": "^7.13.0", + "@babel/register": "^7.13.16", + "babel-core": "^7.0.0-bridge.0", + "chalk": "^4.1.2", + "flow-parser": "0.*", + "graceful-fs": "^4.2.4", + "micromatch": "^3.1.10", + "neo-async": "^2.5.0", + "node-dir": "^0.1.17", + "recast": "^0.20.4", + "temp": "^0.8.4", + "write-file-atomic": "^2.3.0" + }, + "bin": { + "jscodeshift": "bin/jscodeshift.js" + }, + "peerDependencies": { + "@babel/preset-env": "^7.1.6" + } + }, + "node_modules/ts-migrate-plugins/node_modules/recast": { + "version": "0.20.5", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.20.5.tgz", + "integrity": "sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ==", + "dev": true, + "dependencies": { + "ast-types": "0.14.2", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ts-migrate-server": { + "version": "0.1.33", + "resolved": "https://registry.npmjs.org/ts-migrate-server/-/ts-migrate-server-0.1.33.tgz", + "integrity": "sha512-MYHy10yzL2fkb2FHFQ9f54gqc5KkaVthTjtpwS4bTroYCONDelp1hbz5nxKWaP3q2oc3kBVeGuAR91RNI+yK+g==", + "dev": true, + "dependencies": { + "@ts-morph/bootstrap": "^0.16.0", + "pretty-ms": "^7.0.1", + "updatable-log": "^0.2.0" + }, + "peerDependencies": { + "typescript": ">4.0" + } + }, + "node_modules/ts-migrate/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/ts-migrate/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-migrate/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "bin": { - "mime": "cli.js" + "json5": "lib/cli.js" }, "engines": { - "node": ">=4.0.0" + "node": ">=6" } }, - "node_modules/superagent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/ts-migrate/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/ts-migrate/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, - "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/ts-migrate/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "node_modules/ts-migrate/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=0.6" + "node": ">=8" } }, - "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/ts-migrate/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true }, - "node_modules/ts-essentials": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.3.0.tgz", - "integrity": "sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw==", + "node_modules/ts-migrate/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, - "peerDependencies": { - "typescript": ">=4.1.0" + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-migrate/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/ts-node": { @@ -2009,6 +7682,36 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2102,6 +7805,70 @@ "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2110,6 +7877,162 @@ "node": ">= 0.8" } }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/updatable-log": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/updatable-log/-/updatable-log-0.2.0.tgz", + "integrity": "sha512-gR48/mTR6YFB+B1sNoap3nx8HFbEvDl0ej9KhlQTFZdmP8yL5fzFiCUfeHCUf1QvNnXowY1pM9iiGkPKrd0XyQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "figures": "^3.0.0", + "log-update": "^3.3.0" + } + }, + "node_modules/updatable-log/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/updatable-log/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/updatable-log/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/updatable-log/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/updatable-log/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/updatable-log/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/updatable-log/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2118,6 +8041,22 @@ "punycode": "^2.1.0" } }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2131,6 +8070,12 @@ "node": ">= 0.4.0" } }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -2145,6 +8090,27 @@ "node": ">= 0.8" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, "node_modules/winston": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", @@ -2276,6 +8242,15 @@ "node": ">=10" } }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -2298,6 +8273,17 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, "node_modules/ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", diff --git a/package.json b/package.json index 3f5975ab..e60c45a6 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,12 @@ "homepage": "https://github.com/FoxxMD/multi-scrobbler#readme", "dependencies": { "@awaitjs/express": "^0.6.3", + "ajv": "^7.2.4", "body-parser": "^1.19.0", "compare-versions": "^4.1.2", "concat-stream": "^2.0.0", "dayjs": "^1.10.4", "ejs": "^3.1.6", - "ajv": "^7.2.4", "es6-error": "^4.1.1", "express": "^4.17.1", "express-session": "^1.17.2", @@ -55,6 +55,7 @@ "@types/passport": "^1.0.7", "@types/triple-beam": "^1.3.2", "ts-essentials": "^9.1.2", + "ts-migrate": "^0.1.35", "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", "typescript": "^4.6.4", -- 2.51.2 From 03c3f6b6a232433b296f8d2b8760dc2f890cf3ad Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 12:55:08 -0500 Subject: [PATCH 15/64] [ts-migrate][.] Init tsconfig.json file Co-authored-by: ts-migrate <> --- package-lock.json | 7 +++++++ package.json | 1 + 2 files changed, 8 insertions(+) diff --git a/package-lock.json b/package-lock.json index 5cfef882..cd2215df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "winston-transport": "^4.4.0" }, "devDependencies": { + "@tsconfig/node18": "^1.0.1", "@types/express": "^4.17.13", "@types/express-session": "^1.17.4", "@types/node": "^18.0.0", @@ -2289,6 +2290,12 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "node_modules/@tsconfig/node18": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-1.0.1.tgz", + "integrity": "sha512-sNFeK6X2ATlhlvzyH4kKYQlfHXE2f2/wxtB9ClvYXevWpmwkUT7VaSrjIN9E76Qebz8qP5JOJJ9jD3QoD/Z9TA==", + "dev": true + }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", diff --git a/package.json b/package.json index e60c45a6..3465e0e5 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "winston-transport": "^4.4.0" }, "devDependencies": { + "@tsconfig/node18": "^1.0.1", "@types/express": "^4.17.13", "@types/express-session": "^1.17.4", "@types/node": "^18.0.0", -- 2.51.2 From bb7bfc4a6ea86e8606000e12fcdd4ceecd53350c Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 12:56:04 -0500 Subject: [PATCH 16/64] [ts-migrate][.] Init tsconfig.json file Co-authored-by: ts-migrate <> --- tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index c23b1bad..e88849ec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,8 @@ }, "include": [ "**/*.ts", - "**/*.tsx" + "**/*.tsx", + "**/*.js" ], "exclude": [ "node_modules", -- 2.51.2 From e006476b52c863a95aa5bd107e41bad8b146b63f Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 12:56:04 -0500 Subject: [PATCH 17/64] [ts-migrate][.] Rename files from JS/JSX to TS/TSX Co-authored-by: ts-migrate <> --- src/apis/{AbstractApiClient.js => AbstractApiClient.ts} | 0 src/apis/{LastfmApiClient.js => LastfmApiClient.ts} | 0 .../{AbstractScrobbleClient.js => AbstractScrobbleClient.ts} | 0 src/clients/{LastfmScrobbler.js => LastfmScrobbler.ts} | 0 src/clients/{MalojaScrobbler.js => MalojaScrobbler.ts} | 0 src/clients/{ScrobbleClients.js => ScrobbleClients.ts} | 0 src/common/{index.js => index.ts} | 0 src/{index.js => index.ts} | 0 src/server/{middleware.js => middleware.ts} | 0 src/sources/{AbstractSource.js => AbstractSource.ts} | 0 src/sources/{DeezerSource.js => DeezerSource.ts} | 0 src/sources/{JellyfinSource.js => JellyfinSource.ts} | 0 src/sources/{LastfmSource.js => LastfmSource.ts} | 0 src/sources/{MemorySource.js => MemorySource.ts} | 0 src/sources/{PlexSource.js => PlexSource.ts} | 0 src/sources/{ScrobbleSources.js => ScrobbleSources.ts} | 0 src/sources/{SpotifySource.js => SpotifySource.ts} | 0 src/sources/{SubsonicSource.js => SubsonicSource.ts} | 0 src/sources/{TautulliSource.js => TautulliSource.ts} | 0 src/{utils.js => utils.ts} | 0 20 files changed, 0 insertions(+), 0 deletions(-) rename src/apis/{AbstractApiClient.js => AbstractApiClient.ts} (100%) rename src/apis/{LastfmApiClient.js => LastfmApiClient.ts} (100%) rename src/clients/{AbstractScrobbleClient.js => AbstractScrobbleClient.ts} (100%) rename src/clients/{LastfmScrobbler.js => LastfmScrobbler.ts} (100%) rename src/clients/{MalojaScrobbler.js => MalojaScrobbler.ts} (100%) rename src/clients/{ScrobbleClients.js => ScrobbleClients.ts} (100%) rename src/common/{index.js => index.ts} (100%) rename src/{index.js => index.ts} (100%) rename src/server/{middleware.js => middleware.ts} (100%) rename src/sources/{AbstractSource.js => AbstractSource.ts} (100%) rename src/sources/{DeezerSource.js => DeezerSource.ts} (100%) rename src/sources/{JellyfinSource.js => JellyfinSource.ts} (100%) rename src/sources/{LastfmSource.js => LastfmSource.ts} (100%) rename src/sources/{MemorySource.js => MemorySource.ts} (100%) rename src/sources/{PlexSource.js => PlexSource.ts} (100%) rename src/sources/{ScrobbleSources.js => ScrobbleSources.ts} (100%) rename src/sources/{SpotifySource.js => SpotifySource.ts} (100%) rename src/sources/{SubsonicSource.js => SubsonicSource.ts} (100%) rename src/sources/{TautulliSource.js => TautulliSource.ts} (100%) rename src/{utils.js => utils.ts} (100%) diff --git a/src/apis/AbstractApiClient.js b/src/apis/AbstractApiClient.ts similarity index 100% rename from src/apis/AbstractApiClient.js rename to src/apis/AbstractApiClient.ts diff --git a/src/apis/LastfmApiClient.js b/src/apis/LastfmApiClient.ts similarity index 100% rename from src/apis/LastfmApiClient.js rename to src/apis/LastfmApiClient.ts diff --git a/src/clients/AbstractScrobbleClient.js b/src/clients/AbstractScrobbleClient.ts similarity index 100% rename from src/clients/AbstractScrobbleClient.js rename to src/clients/AbstractScrobbleClient.ts diff --git a/src/clients/LastfmScrobbler.js b/src/clients/LastfmScrobbler.ts similarity index 100% rename from src/clients/LastfmScrobbler.js rename to src/clients/LastfmScrobbler.ts diff --git a/src/clients/MalojaScrobbler.js b/src/clients/MalojaScrobbler.ts similarity index 100% rename from src/clients/MalojaScrobbler.js rename to src/clients/MalojaScrobbler.ts diff --git a/src/clients/ScrobbleClients.js b/src/clients/ScrobbleClients.ts similarity index 100% rename from src/clients/ScrobbleClients.js rename to src/clients/ScrobbleClients.ts diff --git a/src/common/index.js b/src/common/index.ts similarity index 100% rename from src/common/index.js rename to src/common/index.ts diff --git a/src/index.js b/src/index.ts similarity index 100% rename from src/index.js rename to src/index.ts diff --git a/src/server/middleware.js b/src/server/middleware.ts similarity index 100% rename from src/server/middleware.js rename to src/server/middleware.ts diff --git a/src/sources/AbstractSource.js b/src/sources/AbstractSource.ts similarity index 100% rename from src/sources/AbstractSource.js rename to src/sources/AbstractSource.ts diff --git a/src/sources/DeezerSource.js b/src/sources/DeezerSource.ts similarity index 100% rename from src/sources/DeezerSource.js rename to src/sources/DeezerSource.ts diff --git a/src/sources/JellyfinSource.js b/src/sources/JellyfinSource.ts similarity index 100% rename from src/sources/JellyfinSource.js rename to src/sources/JellyfinSource.ts diff --git a/src/sources/LastfmSource.js b/src/sources/LastfmSource.ts similarity index 100% rename from src/sources/LastfmSource.js rename to src/sources/LastfmSource.ts diff --git a/src/sources/MemorySource.js b/src/sources/MemorySource.ts similarity index 100% rename from src/sources/MemorySource.js rename to src/sources/MemorySource.ts diff --git a/src/sources/PlexSource.js b/src/sources/PlexSource.ts similarity index 100% rename from src/sources/PlexSource.js rename to src/sources/PlexSource.ts diff --git a/src/sources/ScrobbleSources.js b/src/sources/ScrobbleSources.ts similarity index 100% rename from src/sources/ScrobbleSources.js rename to src/sources/ScrobbleSources.ts diff --git a/src/sources/SpotifySource.js b/src/sources/SpotifySource.ts similarity index 100% rename from src/sources/SpotifySource.js rename to src/sources/SpotifySource.ts diff --git a/src/sources/SubsonicSource.js b/src/sources/SubsonicSource.ts similarity index 100% rename from src/sources/SubsonicSource.js rename to src/sources/SubsonicSource.ts diff --git a/src/sources/TautulliSource.js b/src/sources/TautulliSource.ts similarity index 100% rename from src/sources/TautulliSource.js rename to src/sources/TautulliSource.ts diff --git a/src/utils.js b/src/utils.ts similarity index 100% rename from src/utils.js rename to src/utils.ts -- 2.51.2 From 8f0a75a81b1789ccc4912e926a3711d9752b6a2e Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 8 Feb 2023 12:56:09 -0500 Subject: [PATCH 18/64] [ts-migrate][.] Run TS Migrate Co-authored-by: ts-migrate <> --- .eslintrc | 0 src/apis/AbstractApiClient.ts | 10 ++-- src/apis/LastfmApiClient.ts | 25 ++++++-- src/clients/AbstractScrobbleClient.ts | 25 +++++--- src/clients/LastfmScrobbler.ts | 49 ++++++++++++---- src/clients/MalojaScrobbler.ts | 81 ++++++++++++++++++++------ src/clients/ScrobbleClients.ts | 55 ++++++++++++++---- src/common/index.ts | 1 + src/index.ts | 70 +++++++++++++++++++--- src/server/middleware.ts | 7 ++- src/sources/AbstractSource.ts | 24 ++++++-- src/sources/DeezerSource.ts | 48 +++++++++++++--- src/sources/JellyfinSource.ts | 19 ++++-- src/sources/LastfmSource.ts | 10 ++-- src/sources/MemorySource.ts | 15 +++-- src/sources/PlexSource.ts | 52 +++++++++++------ src/sources/ScrobbleSources.ts | 57 +++++++++++++++--- src/sources/SpotifySource.ts | 50 ++++++++++++---- src/sources/SubsonicSource.ts | 30 ++++++++-- src/sources/TautulliSource.ts | 5 +- src/utils.ts | 83 ++++++++++++++++++--------- 21 files changed, 550 insertions(+), 166 deletions(-) delete mode 100644 .eslintrc diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index e69de29b..00000000 diff --git a/src/apis/AbstractApiClient.ts b/src/apis/AbstractApiClient.ts index ffff7bae..e12cc270 100644 --- a/src/apis/AbstractApiClient.ts +++ b/src/apis/AbstractApiClient.ts @@ -9,11 +9,11 @@ export default class AbstractApiClient { options; logger; - client; - workingCredsPath; - redirectUri; + client: any; + workingCredsPath: any; + redirectUri: any; - constructor(type, name, config = {}, options = {}) { + constructor(type: any, name: any, config = {}, options = {}) { this.type = type; this.name = name; const identifier = `API - ${capitalize(this.type)} - ${name}`; @@ -22,7 +22,7 @@ export default class AbstractApiClient { this.options = options; } - static formatPlayObj = obj => { + static formatPlayObj = (obj: any) => { throw new Error('should be overridden'); } } diff --git a/src/apis/LastfmApiClient.ts b/src/apis/LastfmApiClient.ts index 55eb3213..c1d33797 100644 --- a/src/apis/LastfmApiClient.ts +++ b/src/apis/LastfmApiClient.ts @@ -1,3 +1,4 @@ +// @ts-expect-error TS(7016): Could not find a declaration file for module 'last... Remove this comment to see the full error message import LastFm from "lastfm-node-client"; import AbstractApiClient from "./AbstractApiClient.js"; import dayjs from "dayjs"; @@ -17,12 +18,14 @@ const retryErrors = [ 'rate limit' ] +// @ts-expect-error TS(2417): Class static side 'typeof LastfmApiClient' incorre... Remove this comment to see the full error message export default class LastfmApiClient extends AbstractApiClient { - user; + user: any; - constructor(name, config = {}, options = {}) { + constructor(name: any, config = {}, options = {}) { super('lastfm', name, config, options); + // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{}'... Remove this comment to see the full error message const {redirectUri, apiKey, secret, session, configDir} = config; this.redirectUri = `${redirectUri}?state=${name}`; if (apiKey === undefined) { @@ -32,7 +35,7 @@ export default class LastfmApiClient extends AbstractApiClient { this.client = new LastFm(apiKey, secret, session); } - static formatPlayObj = obj => { + static formatPlayObj = (obj: any) => { const { artist: { // last.fm doesn't seem consistent with which of these properties it returns... @@ -45,6 +48,7 @@ export default class LastfmApiClient extends AbstractApiClient { }, duration, date: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message uts: time, } = {}, '@attr': { @@ -74,9 +78,12 @@ export default class LastfmApiClient extends AbstractApiClient { } } - callApi = async (func, retries = 0) => { + // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message + callApi = async (func: any, retries = 0) => { const { + // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 2, + // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5 } = this.config; @@ -84,6 +91,7 @@ export default class LastfmApiClient extends AbstractApiClient { return await func(this.client); } catch (e) { const { + // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message message, } = e; // for now check for exceptional errors by matching error code text @@ -105,15 +113,19 @@ export default class LastfmApiClient extends AbstractApiClient { } getAuthUrl = () => { + // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{}'... Remove this comment to see the full error message const redir = `${this.config.redirectUri}?state=${this.name}`; + // @ts-expect-error TS(2339): Property 'apiKey' does not exist on type '{}'. return `http://www.last.fm/api/auth/?api_key=${this.config.apiKey}&cb=${encodeURIComponent(redir)}` } - authenticate = async (token) => { + authenticate = async (token: any) => { const sessionRes = await this.client.authGetSession({token}); const { session: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message key: sessionKey, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name, // username } = {} } = sessionRes; @@ -145,9 +157,10 @@ export default class LastfmApiClient extends AbstractApiClient { return false; } try { - const infoResp = await this.callApi(client => client.userGetInfo()); + const infoResp = await this.callApi((client: any) => client.userGetInfo()); const { user: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name, } = {} } = infoResp; diff --git a/src/clients/AbstractScrobbleClient.ts b/src/clients/AbstractScrobbleClient.ts index e835b996..2b986b29 100644 --- a/src/clients/AbstractScrobbleClient.ts +++ b/src/clients/AbstractScrobbleClient.ts @@ -15,7 +15,7 @@ export default class AbstractScrobbleClient { recentScrobbles = []; scrobbledPlayObjs = []; - newestScrobbleTime; + newestScrobbleTime: any; oldestScrobbleTime = dayjs(); tracksScrobbled = 0; @@ -27,13 +27,14 @@ export default class AbstractScrobbleClient { config; logger; - constructor(type, name, config = {}) { + constructor(type: any, name: any, config = {}) { this.type = type; this.name = name; const identifier = `Client ${capitalize(this.type)} - ${name}`; this.logger = createLabelledLogger(identifier, identifier); const { + // @ts-expect-error TS(2339): Property 'options' does not exist on type '{}'. options: { refreshEnabled = true, checkExistingScrobbles = true, @@ -71,8 +72,10 @@ export default class AbstractScrobbleClient { } set initialized(val) { + // @ts-expect-error TS(2367): This condition will always return 'false' since th... Remove this comment to see the full error message if(val === INITIALIZING) { this.#initState = INITIALIZING; + // @ts-expect-error TS(2367): This condition will always return 'false' since th... Remove this comment to see the full error message } else if(val === true || val === INITIALIZED) { this.#initState = INITIALIZED; } else { @@ -112,16 +115,17 @@ export default class AbstractScrobbleClient { return this.lastScrobbleCheck; } - formatPlayObj = obj => { + formatPlayObj = (obj: any) => { this.logger.warn('formatPlayObj should be defined by concrete class!'); return obj; } // time frame is valid as long as the play date for the source track is newer than the oldest play time from the scrobble client // ...this is assuming the scrobble client is returning "most recent" scrobbles - timeFrameIsValid = (playObj, log = false) => { + timeFrameIsValid = (playObj: any, log = false) => { const { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate, } = {}, } = playObj; @@ -132,13 +136,15 @@ export default class AbstractScrobbleClient { return validTime; } - addScrobbledTrack = (playObj, scrobbleResp) => { + addScrobbledTrack = (playObj: any, scrobbleResp: any) => { + // @ts-expect-error TS(2322): Type 'any' is not assignable to type 'never'. this.scrobbledPlayObjs.push({play: playObj, scrobble: this.formatPlayObj(scrobbleResp)}); } - cleanSourceSearchTitle = (playObj) => { + cleanSourceSearchTitle = (playObj: any) => { const { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {}, } = playObj; @@ -146,16 +152,19 @@ export default class AbstractScrobbleClient { return track; }; - findExistingSubmittedPlayObj = (playObj) => { + findExistingSubmittedPlayObj = (playObj: any) => { const { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, } = {} } = playObj; + // @ts-expect-error TS(2339): Property 'play' does not exist on type 'never'. const dtInvariantMatches = this.scrobbledPlayObjs.filter(x => playObjDataMatch(playObj, x.play)); if (dtInvariantMatches.length === 0) { @@ -166,9 +175,11 @@ export default class AbstractScrobbleClient { const { play: { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate: sPlayDate } = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source: playSource } = {}, } = {}, diff --git a/src/clients/LastfmScrobbler.ts b/src/clients/LastfmScrobbler.ts index bf4e863f..46922f4f 100644 --- a/src/clients/LastfmScrobbler.ts +++ b/src/clients/LastfmScrobbler.ts @@ -17,14 +17,16 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { requiresAuth = true; requiresAuthInteraction = true; - constructor(name, config = {}, options = {}) { + constructor(name: any, config = {}, options = {}) { + // @ts-expect-error TS(2554): Expected 2-3 arguments, but got 4. super('lastfm', name, config, options); this.api = new LastfmApiClient(name, config, options) } - formatPlayObj = obj => LastfmApiClient.formatPlayObj(obj); + formatPlayObj = (obj: any) => LastfmApiClient.formatPlayObj(obj); initialize = async () => { + // @ts-expect-error TS(2322): Type 'number' is not assignable to type 'boolean'. this.initialized = INITIALIZING; this.initialized = await this.api.initialize(); return this.initialized; @@ -44,13 +46,13 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { refreshScrobbles = async () => { if (this.refreshEnabled) { this.logger.debug('Refreshing recent scrobbles'); - const resp = await this.api.callApi(client => client.userGetRecentTracks({user: this.api.user, limit: 20, extended: true})); + const resp = await this.api.callApi((client: any) => client.userGetRecentTracks({user: this.api.user, limit: 20, extended: true})); const { recenttracks: { track: list = [], } } = resp; - this.recentScrobbles = list.reduce((acc, x) => { + this.recentScrobbles = list.reduce((acc: any, x: any) => { try { const formatted = LastfmApiClient.formatPlayObj(x); const { @@ -75,6 +77,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { } return acc.concat(formatted); } catch (e) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. this.logger.warn('Failed to format Last.fm recently scrobbled track, omitting from time frame check', {error: e.message}); this.logger.debug('Full api response object:'); this.logger.debug(x); @@ -87,28 +90,31 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { this.newestScrobbleTime = newestScrobbleTime; this.oldestScrobbleTime = oldestScrobbleTime; + // @ts-expect-error TS(2339): Property 'play' does not exist on type 'never'. this.scrobbledPlayObjs = this.scrobbledPlayObjs.filter(x => this.timeFrameIsValid(x.play)); } } this.lastScrobbleCheck = dayjs(); } - cleanSourceSearchTitle = (playObj) => { + cleanSourceSearchTitle = (playObj: any) => { const { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {}, } = playObj; return track.toLocaleLowerCase().trim(); } - alreadyScrobbled = async (playObj, log = false) => { + // @ts-expect-error TS(2416): Property 'alreadyScrobbled' in type 'LastfmScrobbl... Remove this comment to see the full error message + alreadyScrobbled = async (playObj: any, log = false) => { return this.existingScrobble(playObj, (log || this.verboseOptions.match.onMatch)) !== undefined; } - existingScrobble = (playObj, logMatch = false) => { + existingScrobble = (playObj: any, logMatch = false) => { const tr = truncateStringToLength(27); - const scoreTrackOpts = {include: ['track', 'time'], transformers: {track: t => tr(t).padEnd(30)}}; + const scoreTrackOpts = {include: ['track', 'time'], transformers: {track: (t: any) => tr(t).padEnd(30)}}; // return early if we don't care about checking existing if (false === this.checkExistingScrobbles) { @@ -126,6 +132,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { // if we have an submitted play with matching data and play date then we can just return the response from the original scrobble if (existingExactSubmitted !== undefined) { + // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never[... Remove this comment to see the full error message existingScrobble = existingExactSubmitted.scrobble; closestMatch = { @@ -149,15 +156,19 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { // we have have found an existing submission but without an exact date // in which case we can check the scrobble api response against recent scrobbles (also from api) for a more accurate comparison + // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never'... Remove this comment to see the full error message const referenceApiScrobbleResponse = existingDataSubmitted.length > 0 ? existingDataSubmitted[0].scrobble : undefined; const { data: { artists: sourceArtists = [], + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message trackLength, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, } = {}, } = playObj; @@ -169,6 +180,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { const referenceMatch = referenceApiScrobbleResponse !== undefined && playObjDataMatch(x, referenceApiScrobbleResponse); + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message const {data: {playDate: scrobbleTime, track: scrobbleTitle, artists = []} = {}} = x; const playDiffThreshold = source === 'Subsonic' ? 60 : 10; @@ -193,13 +205,14 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { const lowerScrobbleTitle = scrobbleTitle.toLocaleLowerCase().trim(); // because of all this replacing we need a more position-agnostic way of comparing titles so use intersection on title split by spaces // and compare against length of scrobble title - const sourceTitleTerms = new Set(cleanSourceTitle.split(' ').filter(x => x !== '')); + const sourceTitleTerms = new Set(cleanSourceTitle.split(' ').filter((x: any) => x !== '')); const commonTerms = setIntersection(new Set(lowerScrobbleTitle.split(' ')), sourceTitleTerms); titleMatch = commonTerms.size / sourceTitleTerms.size; let artistMatch; - const lowerSourceArtists = sourceArtists.map(x => x.toLocaleLowerCase()); + const lowerSourceArtists = sourceArtists.map((x: any) => x.toLocaleLowerCase()); + // @ts-expect-error TS(2339): Property 'toLocaleLowerCase' does not exist on typ... Remove this comment to see the full error message const lowerScrobbleArtists = artists.map(x => x.toLocaleLowerCase()); artistMatch = setIntersection(new Set(lowerScrobbleArtists), new Set(lowerSourceArtists)).size / artists.length; @@ -234,23 +247,30 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { } if ((existingScrobble !== undefined && this.verboseOptions.match.onMatch) || (existingScrobble === undefined && this.verboseOptions.match.onNoMatch)) { + // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type '{ scor... Remove this comment to see the full error message const closestScrobble = closestMatch.scrobble === undefined ? closestMatch.breakdowns.join(' | ') : `Closest Scrobble: ${buildTrackString(closestMatch.scrobble, scoreTrackOpts)} => ${closestMatch.breakdowns.join(' | ')}`; this.logger.debug(`(Existing Check) Source: ${buildTrackString(playObj, scoreTrackOpts)} => ${closestScrobble}`); } return existingScrobble; } - scrobble = async (playObj) => { + scrobble = async (playObj: any) => { const { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message duration, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, data = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, newFromSource = false, } = {} @@ -274,10 +294,11 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { const scrobblePayload = removeUndefinedKeys(rawPayload); try { - const response = await this.api.callApi(client => client.trackScrobble( + const response = await this.api.callApi((client: any) => client.trackScrobble( scrobblePayload)); const { scrobbles: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message '@attr': { accepted = 0, ignored = 0, @@ -285,11 +306,15 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { }, scrobble: { track: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message '#text': trackName, } = {}, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message timestamp, ignoredMessage: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message code: ignoreCode, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message '#text': ignoreMsg, } = {}, ...rest diff --git a/src/clients/MalojaScrobbler.ts b/src/clients/MalojaScrobbler.ts index 1fb39897..114d0e0c 100644 --- a/src/clients/MalojaScrobbler.ts +++ b/src/clients/MalojaScrobbler.ts @@ -1,4 +1,5 @@ import AbstractScrobbleClient from "./AbstractScrobbleClient.js"; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'supe... Remove this comment to see the full error message import request from 'superagent'; import dayjs from 'dayjs'; import compareVersions from 'compare-versions'; @@ -19,10 +20,12 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { requiresAuth = true; serverIsHealthy = false; - serverVersion; + serverVersion: any; - constructor(name, config = {}, options = {}) { + constructor(name: any, config = {}, options = {}) { + // @ts-expect-error TS(2554): Expected 2-3 arguments, but got 4. super('maloja', name, config, options); + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url, apiKey} = config; if (apiKey === undefined) { this.logger.warn("'apiKey' not found in config! Client will most likely fail when trying to scrobble"); @@ -32,7 +35,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } } - static formatPlayObj(obj, serverVersion = undefined) { + static formatPlayObj(obj: any, serverVersion = undefined) { let artists, title, album, @@ -45,13 +48,18 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { // when the track was scrobbled time: mTime, track: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists: mArtists, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message title: mTitle, album: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name: mAlbum, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists: albumArtists } = {}, // length of the track + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message length: mLength, } = {}, // how long the track was listened to before it was scrobbled @@ -77,7 +85,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { duration = mDuration; time = mTime; } - let artistStrings = artists.reduce((acc, curr) => { + let artistStrings = artists.reduce((acc: any, curr: any) => { let aString; if (typeof curr === 'string') { aString = curr; @@ -101,11 +109,14 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } } - formatPlayObj = obj => MalojaScrobbler.formatPlayObj(obj, this.serverVersion); + formatPlayObj = (obj: any) => MalojaScrobbler.formatPlayObj(obj, this.serverVersion); - callApi = async (req, retries = 0) => { + // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message + callApi = async (req: any, retries = 0) => { const { + // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 1, + // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5 } = this.config; @@ -119,12 +130,18 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { return await this.callApi(req, retries + 1) } const { + // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message message, + // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message body, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message text, } = {}, + // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response, } = e; let msg = response !== undefined ? `API Call failed: Server Response => ${message}` : `API Call failed: ${message}`; @@ -136,6 +153,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { testConnection = async () => { + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url} = this.config; try { const serverInfoResp = await this.callApi(request.get(`${url}/apis/mlj_1/serverinfo`)); @@ -173,12 +191,15 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { testHealth = async () => { + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url} = this.config; try { + // @ts-expect-error TS(2345): Argument of type '{ maxRequestRetries: number; }' ... Remove this comment to see the full error message const serverInfoResp = await this.callApi(request.get(`${url}/apis/mlj_1/serverinfo`), {maxRequestRetries: 0}); const { statusCode, body: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message db_status: { healthy = false, rebuildinprogress = false, @@ -209,6 +230,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { initialize = async () => { // just checking that we can get a connection + // @ts-expect-error TS(2322): Type 'number' is not assignable to type 'boolean'. this.initialized = INITIALIZING; this.initialized = await this.testConnection(); return this.initialized; @@ -216,6 +238,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { testAuth = async () => { + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url, apiKey} = this.config; try { const resp = await this.callApi(request @@ -225,6 +248,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { const { status, body: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status: bodyStatus, } = {}, body = {}, @@ -242,6 +266,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { }); } } catch (e) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. if(e.status === 403) { // may be an older version that doesn't support auth readiness before db upgrade // and if it was before api was accessible during db build then test would fail during testConnection() @@ -284,6 +309,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { refreshScrobbles = async () => { if (this.refreshEnabled) { this.logger.debug('Refreshing recent scrobbles'); + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url} = this.config; const resp = await this.callApi(request.get(`${url}/apis/mlj_1/scrobbles?max=20`)); const { @@ -291,22 +317,24 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { list = [], } = {}, } = resp; - this.recentScrobbles = list.map(x => this.formatPlayObj(x)).sort(sortByPlayDate); + this.recentScrobbles = list.map((x: any) => this.formatPlayObj(x)).sort(sortByPlayDate); if (this.recentScrobbles.length > 0) { const [{data: {playDate: newestScrobbleTime = dayjs()} = {}} = {}] = this.recentScrobbles.slice(-1); const [{data: {playDate: oldestScrobbleTime = dayjs()} = {}} = {}] = this.recentScrobbles.slice(0, 1); this.newestScrobbleTime = newestScrobbleTime; this.oldestScrobbleTime = oldestScrobbleTime; + // @ts-expect-error TS(2339): Property 'play' does not exist on type 'never'. this.scrobbledPlayObjs = this.scrobbledPlayObjs.filter(x => this.timeFrameIsValid(x.play)); } } this.lastScrobbleCheck = dayjs(); } - cleanSourceSearchTitle = (playObj) => { + cleanSourceSearchTitle = (playObj: any) => { const { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, artists: sourceArtists = [], } = {}, @@ -314,8 +342,8 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { let lowerTitle = track.toLocaleLowerCase(); lowerTitle = feat.reduce((acc, curr) => acc.replace(curr, ''), lowerTitle); // also remove [artist] from the track if found since that gets removed as well - const lowerArtists = sourceArtists.map(x => x.toLocaleLowerCase()); - lowerTitle = lowerArtists.reduce((acc, curr) => acc.replace(curr, ''), lowerTitle); + const lowerArtists = sourceArtists.map((x: any) => x.toLocaleLowerCase()); + lowerTitle = lowerArtists.reduce((acc: any, curr: any) => acc.replace(curr, ''), lowerTitle); // remove any whitespace in parenthesis lowerTitle = lowerTitle.replace("\\s+(?=[^()]*\\))", '') @@ -327,13 +355,14 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { return lowerTitle; } - alreadyScrobbled = async (playObj, log = false) => { + // @ts-expect-error TS(2416): Property 'alreadyScrobbled' in type 'MalojaScrobbl... Remove this comment to see the full error message + alreadyScrobbled = async (playObj: any, log = false) => { return this.existingScrobble(playObj, (log || this.verboseOptions.match.onMatch)) !== undefined; } - existingScrobble = (playObj, logMatch = false) => { + existingScrobble = (playObj: any, logMatch = false) => { const tr = truncateStringToLength(27); - const scoreTrackOpts = {include: ['track', 'time'], transformers: {track: t => tr(t).padEnd(30)}}; + const scoreTrackOpts = {include: ['track', 'time'], transformers: {track: (t: any) => tr(t).padEnd(30)}}; // return early if we don't care about checking existing if (false === this.checkExistingScrobbles) { @@ -351,6 +380,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { // if we have an submitted play with matching data and play date then we can just return the response from the original scrobble if (existingExactSubmitted !== undefined) { + // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never[... Remove this comment to see the full error message existingScrobble = existingExactSubmitted.scrobble; closestMatch = { @@ -374,15 +404,19 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { // we have have found an existing submission but without an exact date // in which case we can check the scrobble api response against recent scrobbles (also from api) for a more accurate comparison + // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never'... Remove this comment to see the full error message const referenceApiScrobbleResponse = existingDataSubmitted.length > 0 ? existingDataSubmitted[0].scrobble : undefined; const { data: { artists: sourceArtists = [], + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message trackLength, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, } = {}, } = playObj; @@ -394,6 +428,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { const referenceMatch = referenceApiScrobbleResponse !== undefined && playObjDataMatch(x, referenceApiScrobbleResponse); + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message const {data: {playDate: scrobbleTime, track: scrobbleTitle, artists = []} = {}} = x; const playDiffThreshold = source === 'Subsonic' ? 60 : 10; @@ -418,13 +453,14 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { const lowerScrobbleTitle = scrobbleTitle.toLocaleLowerCase().trim(); // because of all this replacing we need a more position-agnostic way of comparing titles so use intersection on title split by spaces // and compare against length of scrobble title - const sourceTitleTerms = new Set(cleanSourceTitle.split(' ').filter(x => x !== '')); + const sourceTitleTerms = new Set(cleanSourceTitle.split(' ').filter((x: any) => x !== '')); const commonTerms = setIntersection(new Set(lowerScrobbleTitle.split(' ')), sourceTitleTerms); titleMatch = commonTerms.size / sourceTitleTerms.size; let artistMatch; - const lowerSourceArtists = sourceArtists.map(x => x.toLocaleLowerCase()); + const lowerSourceArtists = sourceArtists.map((x: any) => x.toLocaleLowerCase()); + // @ts-expect-error TS(2339): Property 'toLocaleLowerCase' does not exist on typ... Remove this comment to see the full error message const lowerScrobbleArtists = artists.map(x => x.toLocaleLowerCase()); artistMatch = setIntersection(new Set(lowerScrobbleArtists), new Set(lowerSourceArtists)).size / artists.length; @@ -459,24 +495,32 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } if ((existingScrobble !== undefined && this.verboseOptions.match.onMatch) || (existingScrobble === undefined && this.verboseOptions.match.onNoMatch)) { + // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type '{ scor... Remove this comment to see the full error message const closestScrobble = closestMatch.scrobble === undefined ? closestMatch.breakdowns.join(' | ') : `Closest Scrobble: ${buildTrackString(closestMatch.scrobble, scoreTrackOpts)} => ${closestMatch.breakdowns.join(' | ')}`; this.logger.debug(`(Existing Check) Source: ${buildTrackString(playObj, scoreTrackOpts)} => ${closestScrobble}`); } return existingScrobble; } - scrobble = async (playObj) => { + scrobble = async (playObj: any) => { + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url, apiKey} = this.config; const { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message duration, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, newFromSource = false, } = {} @@ -496,10 +540,12 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { try { // 3.0.3 has a BC for something (maybe seconds => length ?) -- see #42 in repo if(this.serverVersion === undefined || compareVersions(this.serverVersion, '3.0.2') > 0) { + // @ts-expect-error TS(2339): Property 'artists' does not exist on type '{ title... Remove this comment to see the full error message scrobbleData.artists = artists; } else { // maloja seems to detect this deliminator much better than commas // also less likely artist has a forward slash in their name than a comma + // @ts-expect-error TS(2339): Property 'artist' does not exist on type '{ title:... Remove this comment to see the full error message scrobbleData.artist = artists.join(' / '); } @@ -512,6 +558,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { if(this.serverVersion === undefined || compareVersions(this.serverVersion, '3.0.0') >= 0) { const { body: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {} } = response; @@ -526,6 +573,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { const { album: malojaAlbum = {}, } = track; + // @ts-expect-error TS(2339): Property 'track' does not exist on type '{}'. scrobbleResponse.track.album = { ...malojaAlbum, name: album @@ -533,6 +581,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } } else { const {body: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track: { time: mTime = playDate.unix(), duration: mDuration = duration, diff --git a/src/clients/ScrobbleClients.ts b/src/clients/ScrobbleClients.ts index 26cc3c7e..22d1d698 100644 --- a/src/clients/ScrobbleClients.ts +++ b/src/clients/ScrobbleClients.ts @@ -18,16 +18,18 @@ export default class ScrobbleClients { clientTypes = ['maloja','lastfm']; - constructor(configDir) { + constructor(configDir: any) { this.configDir = configDir; this.logger = createLabelledLogger('scrobblers', 'Scrobblers'); } - getByName = (name) => { + getByName = (name: any) => { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. return this.clients.find(x => x.name === name); } - getByType = (type) => { + getByType = (type: any) => { + // @ts-expect-error TS(2339): Property 'type' does not exist on type 'never'. return this.clients.filter(x => x.type === type); } @@ -48,7 +50,7 @@ export default class ScrobbleClients { clientDefaults: cd = {}, } = configFile; clientDefaults = cd; - const validMainConfigs = mainConfigClientConfigs.reduce((acc, curr, i) => { + const validMainConfigs = mainConfigClientConfigs.reduce((acc: any, curr: any, i: any) => { if(curr === null) { this.logger.error(`The client config entry at index ${i} in config.json is null but should be an object, will not parse`); return acc; @@ -166,21 +168,24 @@ export default class ScrobbleClients { // all client configs are minimally valid // now check that names are unique - const nameGroupedConfigs = validConfigs.reduce((acc, curr) => { + const nameGroupedConfigs = validConfigs.reduce((acc: any, curr: any) => { const {name = 'unnamed'} = curr; const {[name]: n = []} = acc; return {...acc, [name]: [...n, curr]}; }, {}); - let noConflictConfigs = []; + let noConflictConfigs: any = []; for (const [name, configs] of Object.entries(nameGroupedConfigs)) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. if (configs.length > 1) { - const sources = configs.map(c => `Config object from ${c.source} of type [${c.type}]`); + // @ts-expect-error TS(2571): Object is of type 'unknown'. + const sources = configs.map((c: any) => `Config object from ${c.source} of type [${c.type}]`); this.logger.error(`The following clients will not be built because of config naming conflicts (they have the same name of "${name}"): ${sources.join('\n')}`); if (name === 'unnamed') { this.logger.info('HINT: "unnamed" configs occur when using ENVs, if a multi-user mode config does not have a "name" property, or if a config is built in single-user mode'); } } else { + // @ts-expect-error TS(2488): Type 'unknown' must have a '[Symbol.iterator]()' m... Remove this comment to see the full error message noConflictConfigs = [...noConflictConfigs, ...configs]; } } @@ -201,7 +206,7 @@ ${sources.join('\n')}`); } } - addClient = async (clientConfig, defaults = {}) => { + addClient = async (clientConfig: any, defaults = {}) => { const isValidConfig = isValidConfigStructure(clientConfig, {name: true, data: true, type: true}); if (isValidConfig !== true) { throw new Error(`Config object from ${clientConfig.source || 'unknown'} with name [${clientConfig.name || 'unnamed'}] of type [${clientConfig.type || 'unknown'}] has errors: ${isValidConfig.join(' | ')}`) @@ -228,7 +233,7 @@ ${sources.join('\n')}`); } if(newClient.initialized === false) { this.logger.debug(`(${name}) Attempting ${type} initialization...`); - if (await newClient.initialize() === false) { + if ((await newClient.initialize()) === false) { this.logger.error(`(${name}) ${type} client failed to initialize. Client needs to be successfully initialized before scrobbling.`); } else { this.logger.info(`(${name}) ${type} client initialized`); @@ -248,6 +253,7 @@ ${sources.join('\n')}`); this.logger.info(`(${name}) ${type} client auth OK`); } } + // @ts-expect-error TS(2345): Argument of type 'MalojaScrobbler | LastfmScrobble... Remove this comment to see the full error message this.clients.push(newClient); } @@ -256,56 +262,77 @@ ${sources.join('\n')}`); * @param {{scrobbleFrom, scrobbleTo, forceRefresh: boolean}|{scrobbleFrom, scrobbleTo}} options * @returns {Array} */ - scrobble = async (data, options = {}) => { + scrobble = async (data: any, options = {}) => { const playObjs = Array.isArray(data) ? data : [data]; const { + // @ts-expect-error TS(2339): Property 'forceRefresh' does not exist on type '{}... Remove this comment to see the full error message forceRefresh = false, + // @ts-expect-error TS(2339): Property 'checkTime' does not exist on type '{}'. checkTime = dayjs(), + // @ts-expect-error TS(2339): Property 'scrobbleTo' does not exist on type '{}'. scrobbleTo = [], + // @ts-expect-error TS(2339): Property 'scrobbleFrom' does not exist on type '{}... Remove this comment to see the full error message scrobbleFrom = 'source', } = options; - const tracksScrobbled = []; + const tracksScrobbled: any = []; if (this.clients.length === 0) { this.logger.warn('Cannot scrobble! No clients are configured.'); } for (const client of this.clients) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. if (scrobbleTo.length > 0 && !scrobbleTo.includes(client.name)) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.debug(`Client '${client.name}' was filtered out by '${scrobbleFrom}'`); continue; } + // @ts-expect-error TS(2339): Property 'initialized' does not exist on type 'nev... Remove this comment to see the full error message if(!client.initialized) { + // @ts-expect-error TS(2339): Property 'initializing' does not exist on type 'ne... Remove this comment to see the full error message if(client.initializing) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because it is still initializing`); continue; } + // @ts-expect-error TS(2339): Property 'initialize' does not exist on type 'neve... Remove this comment to see the full error message if(!(await client.initialize())) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because it could not be initialized`); continue; } } + // @ts-expect-error TS(2339): Property 'requiresAuth' does not exist on type 'ne... Remove this comment to see the full error message if(client.requiresAuth && !client.authed) { + // @ts-expect-error TS(2339): Property 'requiresAuthInteraction' does not exist ... Remove this comment to see the full error message if (client.requiresAuthInteraction) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because user interaction is required for authentication`); continue; + // @ts-expect-error TS(2339): Property 'testAuth' does not exist on type 'never'... Remove this comment to see the full error message } else if (!(await client.testAuth())) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because auth test failed`); continue; } } + // @ts-expect-error TS(2339): Property 'isReady' does not exist on type 'never'. if(!(await client.isReady())) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because it is not ready`); continue; } + // @ts-expect-error TS(2339): Property 'scrobblesLastCheckedAt' does not exist o... Remove this comment to see the full error message if (forceRefresh || client.scrobblesLastCheckedAt().unix() < checkTime.unix()) { try { + // @ts-expect-error TS(2339): Property 'refreshScrobbles' does not exist on type... Remove this comment to see the full error message await client.refreshScrobbles(); } catch(e) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.error(`Encountered error while refreshing scrobbles for ${client.name}`); this.logger.error(e); } @@ -317,19 +344,25 @@ ${sources.join('\n')}`); newFromSource = false, } = {} } = playObj; + // @ts-expect-error TS(2339): Property 'timeFrameIsValid' does not exist on type... Remove this comment to see the full error message if (client.timeFrameIsValid(playObj, newFromSource) && !(await client.alreadyScrobbled(playObj, newFromSource))) { + // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never'... Remove this comment to see the full error message await client.scrobble(playObj) + // @ts-expect-error TS(2339): Property 'tracksScrobbled' does not exist on type ... Remove this comment to see the full error message client.tracksScrobbled++; // since this is what we return to the source only add to tracksScrobbled if not already in array // (source should only know that a track was scrobbled (binary) -- doesn't care if it was scrobbled more than once + // @ts-expect-error TS(7006): Parameter 'x' implicitly has an 'any' type. if(!tracksScrobbled.some(x => playObjDataMatch(x, playObj) && x.data.playDate === playObj.data.playDate)) { tracksScrobbled.push(playObj); } } } catch(e) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.error(`Encountered error while in scrobble loop for ${client.name}`); this.logger.error(e); // for now just stop scrobbling plays for this client and move on. the client should deal with logging the issue + // @ts-expect-error TS(2571): Object is of type 'unknown'. if(e.continueScrobbling !== true) { break; } diff --git a/src/common/index.ts b/src/common/index.ts index bce402bd..01f1f5d0 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -12,6 +12,7 @@ export const READY = 2; export const readyStates = [NOT_READY, GETTING_READY, READY]; +// @ts-expect-error TS(1343): The 'import.meta' meta-property is only allowed wh... Remove this comment to see the full error message const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); diff --git a/src/index.ts b/src/index.ts index 05d0bc7a..9bf2bb45 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,9 @@ import passport from 'passport'; import session from 'express-session'; import {Writable} from 'stream'; import 'winston-daily-rotate-file'; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'form... Remove this comment to see the full error message import formidable from 'formidable'; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'conc... Remove this comment to see the full error message import concatStream from 'concat-stream'; import { buildTrackString, @@ -40,6 +42,7 @@ const app = addAsync(express()); const router = Router(); const port = process.env.PORT ?? 9078; +// @ts-expect-error TS(1378): Top-level 'await' expressions are only allowed whe... Remove this comment to see the full error message const server = await app.listen(port) const io = new Server(server); @@ -52,7 +55,7 @@ app.use(passport.session()); const {transports} = winston; -let output = [] +let output: any = [] const stream = new Writable() stream._write = (chunk, encoding, next) => { let formatString = chunk.toString().replace('\n', '
') @@ -96,6 +99,7 @@ const myTransports = [ ]; if (typeof logPath === 'string') { + // @ts-expect-error TS(2345): Argument of type 'DailyRotateFile' is not assignab... Remove this comment to see the full error message myTransports.push(rotateTransport); } @@ -123,7 +127,9 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) // setup defaults for other configs and general config const { + // @ts-expect-error TS(2339): Property 'spotify' does not exist on type '{}'. spotify, + // @ts-expect-error TS(2339): Property 'plex' does not exist on type '{}'. plex, } = config || {}; @@ -158,6 +164,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) data: plex }); } + // @ts-expect-error TS(2345): Argument of type '{ type: string; name: string; so... Remove this comment to see the full error message await scrobbleSources.buildSourcesFromConfig(deprecatedConfigs); const clientCheckMiddle = makeClientCheckMiddle(scrobbleClients); @@ -167,15 +174,19 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const lastfmSources = scrobbleSources.getByType('lastfm'); const lastfmScrobbles = scrobbleClients.getByType('lastfm'); + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. const scrobblerNames = lastfmScrobbles.map(x => x.name); + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. const nameColl = lastfmSources.filter(x => scrobblerNames.includes(x.name)); if(nameColl.length > 0) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. logger.warn(`Last.FM source and clients have same names [${nameColl.map(x => x.name).join(',')}] -- this may cause issues`); } // initialize deezer strategies const deezerSources = scrobbleSources.getByType('deezer'); for(const d of deezerSources) { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. passport.use(`deezer-${d.name}`, d.generatePassportStrategy()); } @@ -208,12 +219,16 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) authed, }; if(!initialized) { + // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = 'Not Initialized'; } else if(requiresAuth && !authed) { + // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = requiresAuthInteraction ? 'Auth Interaction Required' : 'Authentication Failed Or Not Attempted' } else if(canPoll) { + // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = polling ? 'Running' : 'Idle'; } else { + // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = tracksDiscovered > 0 ? 'Received Data' : 'Awaiting Data' } return base; @@ -236,10 +251,13 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) hasAuth: requiresAuth, }; if(!initialized) { + // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = 'Not Initialized'; } else if(requiresAuth && !authed) { + // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = requiresAuthInteraction ? 'Auth Interaction Required' : 'Authentication Failed Or Not Attempted' } else { + // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = tracksScrobbled > 0 ? 'Received Data' : 'Awaiting Data'; } return base; @@ -256,16 +274,18 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) }); }) - app.postAsync('/tautulli', async function (req, res) { + app.postAsync('/tautulli', async function(this: any, req, res) { const payload = TautulliSource.formatPlayObj(req.body, true); // try to get config name from payload if (req.body.scrobblerConfig !== undefined) { const source = scrobbleSources.getByName(req.body.scrobblerConfig); if (source !== undefined) { + // @ts-expect-error TS(2339): Property 'type' does not exist on type 'never'. if (source.type !== 'tautulli') { this.logger.warn(`Tautulli event specified a config name but the configured source was not a Tautulli type: ${req.body.scrobblerConfig}`); return res.send('OK'); } else { + // @ts-expect-error TS(2339): Property 'handle' does not exist on type 'never'. await source.handle(payload, scrobbleClients); return res.send('OK'); } @@ -277,6 +297,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) // if none specified we'll iterate through all tautulli sources and hopefully the user has configured them with filters const tSources = scrobbleSources.getByType('tautulli'); for (const source of tSources) { + // @ts-expect-error TS(2339): Property 'handle' does not exist on type 'never'. await source.handle(payload, scrobbleClients); } @@ -286,6 +307,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const plexMiddle = plexRequestMiddle(); const plexLog = createLabelledLogger('plexReq', 'Plex Request'); app.postAsync('/plex', plexMiddle, async function (req, res) { + // @ts-expect-error TS(2339): Property 'payload' does not exist on type 'Request... Remove this comment to see the full error message const { payload } = req; if(payload === undefined) { @@ -300,6 +322,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) } for (const source of pSources) { + // @ts-expect-error TS(2339): Property 'handle' does not exist on type 'never'. await source.handle(playObj, scrobbleClients); } } @@ -313,6 +336,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const playObj = JellyfinSource.formatPlayObj(req.body, true); const pSources = scrobbleSources.getByType('jellyfin'); for (const source of pSources) { + // @ts-expect-error TS(2339): Property 'handle' does not exist on type 'never'. await source.handle(playObj, scrobbleClients); } res.send('OK'); @@ -321,6 +345,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) app.use('/client/auth', clientCheckMiddle); app.getAsync('/client/auth', async function (req, res) { const { + // @ts-expect-error TS(2339): Property 'scrobbleClient' does not exist on type '... Remove this comment to see the full error message scrobbleClient, } = req; @@ -336,7 +361,9 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) app.use('/source/auth', sourceCheckMiddle); app.getAsync('/source/auth', async function (req, res, next) { const { + // @ts-expect-error TS(2339): Property 'scrobbleSource' does not exist on type '... Remove this comment to see the full error message scrobbleSource: source, + // @ts-expect-error TS(2339): Property 'sourceName' does not exist on type 'Requ... Remove this comment to see the full error message sourceName: name, } = req; @@ -353,6 +380,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) res.redirect(source.api.getAuthUrl()); break; case 'deezer': + // @ts-expect-error TS(2339): Property 'deezerSource' does not exist on type 'Se... Remove this comment to see the full error message req.session.deezerSource = name; return passport.authenticate(`deezer-${source.name}`)(req,res,next); default: @@ -363,6 +391,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) app.use('/poll', sourceCheckMiddle); app.getAsync('/poll', async function (req, res) { const { + // @ts-expect-error TS(2339): Property 'scrobbleSource' does not exist on type '... Remove this comment to see the full error message scrobbleSource: source, } = req; @@ -377,6 +406,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) app.use('/recent', sourceCheckMiddle); app.getAsync('/recent', async function (req, res) { const { + // @ts-expect-error TS(2339): Property 'scrobbleSource' does not exist on type '... Remove this comment to see the full error message scrobbleSource: source, } = req; if (!source.canPoll) { @@ -384,12 +414,13 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) } const result = await source.getRecentlyPlayed({formatted: true}); - const artistTruncFunc = truncateStringToLength(Math.min(40, longestString(result.map(x => x.data.artists.join(' / ')).flat()))); - const trackLength = longestString(result.map(x => x.data.track)) - const plays = result.map((x) => { + const artistTruncFunc = truncateStringToLength(Math.min(40, longestString(result.map((x: any) => x.data.artists.join(' / ')).flat()))); + const trackLength = longestString(result.map((x: any) => x.data.track)) + const plays = result.map((x: any) => { const { meta: { url: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message web } = {} } = {} @@ -397,8 +428,8 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const buildOpts = { include: ['time', 'timeFromNow', 'track', 'artist'], transformers: { - artists: a => artistTruncFunc(a.join(' / ')).padEnd(33), - track: t => t.padEnd(trackLength) + artists: (a: any) => artistTruncFunc(a.join(' / ')).padEnd(33), + track: (t: any) => t.padEnd(trackLength) } } if (web !== undefined) { @@ -414,14 +445,18 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) for (const [setting, val] of Object.entries(req.query)) { switch (setting) { case 'limit': + // @ts-expect-error TS(2345): Argument of type 'string | ParsedQs | string[] | P... Remove this comment to see the full error message logConfig.limit = Number.parseInt(val); break; case 'sort': + // @ts-expect-error TS(2322): Type 'string | ParsedQs | string[] | ParsedQs[] | ... Remove this comment to see the full error message logConfig.sort = val; break; case 'level': + // @ts-expect-error TS(2322): Type 'string | ParsedQs | string[] | ParsedQs[] | ... Remove this comment to see the full error message logConfig.level = val; for (const [key, logger] of winston.loggers.loggers) { + // @ts-expect-error TS(2322): Type 'string | ParsedQs | string[] | ParsedQs[] | ... Remove this comment to see the full error message logger.level = val; } break; @@ -438,16 +473,22 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) // something about the deezer passport strategy makes express continue with the response even though it should wait for accesstoken callback and userprofile fetching // so to get around this add an additional middleware that loops/sleeps until we should have fetched everything ¯\_(ツ)_/¯ app.getAsync(/.*deezer\/callback*$/, function (req, res, next) { + // @ts-expect-error TS(2339): Property 'deezerSource' does not exist on type 'Se... Remove this comment to see the full error message const entity = scrobbleSources.getByName(req.session.deezerSource); + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const passportFunc = passport.authenticate(`deezer-${entity.name}`, {session: false}); return passportFunc(req, res, next); }, async function (req, res) { + // @ts-expect-error TS(2339): Property 'deezerSource' does not exist on type 'Se... Remove this comment to see the full error message let entity = scrobbleSources.getByName(req.session.deezerSource); for(let i = 0; i < 3; i++) { + // @ts-expect-error TS(2532): Object is possibly 'undefined'. if(entity.error !== undefined) { return res.send('Error with deezer credentials storage'); + // @ts-expect-error TS(2532): Object is possibly 'undefined'. } else if(entity.config.accessToken !== undefined) { // start polling + // @ts-expect-error TS(2532): Object is possibly 'undefined'. entity.poll(entity.clients) return res.redirect('/'); } else { @@ -474,10 +515,13 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) entity = scrobbleSources.getByName(state); } try { + // @ts-expect-error TS(2532): Object is possibly 'undefined'. await entity.api.authenticate(token); + // @ts-expect-error TS(2532): Object is possibly 'undefined'. await entity.initialize(); return res.send('OK'); } catch (e) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. return res.send(e.message); } } else { @@ -485,10 +529,13 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) // but eventually should update all source callbacks to url specific URLS to avoid ambiguity... // wish we could use state param to identify name/source but not all auth strategies and auth provides may provide access to that logger.info('Received auth code callback from Spotify', {label: 'Spotify'}); + // @ts-expect-error TS(2554): Expected 1 arguments, but got 2. const source = scrobbleSources.getByName(state, 'spotify'); + // @ts-expect-error TS(2532): Object is possibly 'undefined'. const tokenResult = await source.handleAuthCodeCallback(req.query); let responseContent = 'OK'; if (tokenResult === true) { + // @ts-expect-error TS(2532): Object is possibly 'undefined'. source.poll(scrobbleClients); } else { responseContent = tokenResult; @@ -506,25 +553,34 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) }); let anyNotReady = false; + // @ts-expect-error TS(2339): Property 'canPoll' does not exist on type 'never'. for (const source of scrobbleSources.sources.filter(x => x.canPoll === true)) { await sleep(1500); // stagger polling by 1.5 seconds so that log messages for each source don't get mixed up + // @ts-expect-error TS(2339): Property 'type' does not exist on type 'never'. switch (source.type) { case 'spotify': + // @ts-expect-error TS(2339): Property 'spotifyApi' does not exist on type 'neve... Remove this comment to see the full error message if (source.spotifyApi !== undefined) { + // @ts-expect-error TS(2339): Property 'spotifyApi' does not exist on type 'neve... Remove this comment to see the full error message if (source.spotifyApi.getAccessToken() === undefined) { anyNotReady = true; } else { + // @ts-expect-error TS(2339): Property 'poll' does not exist on type 'never'. source.poll(scrobbleClients); } } break; case 'lastfm': + // @ts-expect-error TS(2339): Property 'initialized' does not exist on type 'nev... Remove this comment to see the full error message if(source.initialized === true) { + // @ts-expect-error TS(2339): Property 'poll' does not exist on type 'never'. source.poll(scrobbleClients); } break; default: + // @ts-expect-error TS(2339): Property 'poll' does not exist on type 'never'. if (source.poll !== undefined) { + // @ts-expect-error TS(2339): Property 'poll' does not exist on type 'never'. source.poll(scrobbleClients); } } diff --git a/src/server/middleware.ts b/src/server/middleware.ts index ef69b15d..5aa9c8be 100644 --- a/src/server/middleware.ts +++ b/src/server/middleware.ts @@ -1,7 +1,9 @@ -export const makeSourceCheckMiddle = sources => (req, res, next) => { +export const makeSourceCheckMiddle = (sources: any) => (req: any, res: any, next: any) => { const { query: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message type } = {} } = req; @@ -21,9 +23,10 @@ export const makeSourceCheckMiddle = sources => (req, res, next) => { next(); } -export const makeClientCheckMiddle = clients => (req, res, next) => { +export const makeClientCheckMiddle = (clients: any) => (req: any, res: any, next: any) => { const { query: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name } = {} } = req; diff --git a/src/sources/AbstractSource.ts b/src/sources/AbstractSource.ts index b92d84b9..c4885559 100644 --- a/src/sources/AbstractSource.ts +++ b/src/sources/AbstractSource.ts @@ -21,7 +21,7 @@ export default class AbstractSource { pollRetries = 0; tracksDiscovered = 0; - constructor(type, name, config = {}, clients = []) { + constructor(type: any, name: any, config = {}, clients = []) { this.type = type; this.name = name; this.identifier = `Source - ${capitalize(this.type)} - ${name}`; @@ -49,15 +49,15 @@ export default class AbstractSource { // by default if the track was recently played it is valid // this is useful for sources where the track doesn't have complete information like Subsonic // TODO make this more descriptive? or move it elsewhere - recentlyPlayedTrackIsValid = (playObj) => { + recentlyPlayedTrackIsValid = (playObj: any) => { return true; } - poll = async (allClients) => { + poll = async (allClients: any) => { await this.startPolling(allClients); } - startPolling = async (allClients) => { + startPolling = async (allClients: any) => { if(this.requiresAuthInteraction && !this.authed) { this.logger.error('Cannot start polling because user interaction is required for authentication'); return; @@ -66,7 +66,9 @@ export default class AbstractSource { this.pollRetries = 0; const { + // @ts-expect-error TS(2339): Property 'maxPollRetries' does not exist on type '... Remove this comment to see the full error message maxPollRetries = 0, + // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5, } = this.config; @@ -92,7 +94,7 @@ export default class AbstractSource { /** * @param {ScrobbleClients} allClients */ - doPolling = async (allClients) => { + doPolling = async (allClients: any) => { if (this.polling === true) { return; } @@ -103,6 +105,7 @@ export default class AbstractSource { try { this.polling = true; while (true) { + // @ts-expect-error TS(2367): This condition will always return 'false' since th... Remove this comment to see the full error message if(this.polling === false) { this.logger.info('Stopped polling due to user input'); break; @@ -115,18 +118,24 @@ export default class AbstractSource { let closeToInterval = false; const now = dayjs(); + // @ts-expect-error TS(2769): No overload matches this call. const playInfo = playObjs.reduce((acc, playObj) => { if(this.recentlyPlayedTrackIsValid(playObj)) { - const {data: {playDate} = {}} = playObj; + const {data: { + playDate + }: any = {}} = playObj; + // @ts-expect-error TS(2339): Property 'unix' does not exist on type 'never'. if (playDate.unix() > lastTrackPlayedAt.unix()) { newTracksFound = true; this.logger.info(`New Track => ${buildTrackString(playObj)}`); if (closeToInterval === false) { + // @ts-expect-error TS(2339): Property 'unix' does not exist on type 'never'. closeToInterval = Math.abs(now.unix() - playDate.unix()) < 5; } return { + // @ts-expect-error TS(2698): Spread types may only be created from object types... Remove this comment to see the full error message plays: [...acc.plays, {...playObj, meta: {...playObj.meta, newFromSource: true}}], lastTrackPlayedAt: playDate } @@ -138,7 +147,9 @@ export default class AbstractSource { } return acc; }, {plays: [], lastTrackPlayedAt}); + // @ts-expect-error TS(2339): Property 'plays' does not exist on type 'never'. playObjs = playInfo.plays; + // @ts-expect-error TS(2339): Property 'lastTrackPlayedAt' does not exist on typ... Remove this comment to see the full error message lastTrackPlayedAt = playInfo.lastTrackPlayedAt; if (closeToInterval) { @@ -178,6 +189,7 @@ export default class AbstractSource { this.tracksDiscovered += scrobbleResult.length; } + // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. const {interval = 30, checkActiveFor = 300, maxSleep = 300} = this.config; let sleepTime = interval; diff --git a/src/sources/DeezerSource.ts b/src/sources/DeezerSource.ts index 16b18340..886db704 100644 --- a/src/sources/DeezerSource.ts +++ b/src/sources/DeezerSource.ts @@ -1,5 +1,7 @@ +// @ts-expect-error TS(7016): Could not find a declaration file for module 'supe... Remove this comment to see the full error message import request from 'superagent'; import {parseRetryAfterSecsFromObj, readJson, sleep, sortByPlayDate, writeFile} from "../utils.js"; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'pass... Remove this comment to see the full error message import {Strategy as DeezerStrategy} from 'passport-deezer'; import AbstractSource from "./AbstractSource.js"; import dayjs from "dayjs"; @@ -9,18 +11,21 @@ export default class DeezerSource extends AbstractSource { localUrl; workingCredsPath; configDir; - error; + error: any; requiresAuth = true; requiresAuthInteraction = true; baseUrl = 'https://api.deezer.com'; - constructor(name, config = {}, clients = []) { + constructor(name: any, config = {}, clients = []) { super('deezer', name, config, clients); const { + // @ts-expect-error TS(2339): Property 'localUrl' does not exist on type '{}'. localUrl, + // @ts-expect-error TS(2339): Property 'configDir' does not exist on type '{}'. configDir, + // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. interval = 60, } = config; @@ -28,6 +33,7 @@ export default class DeezerSource extends AbstractSource { this.logger.warn('Interval should be above 30 seconds...😬'); } + // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. this.config.interval = interval; this.configDir = configDir; @@ -36,10 +42,11 @@ export default class DeezerSource extends AbstractSource { this.canPoll = true; } - static formatPlayObj(obj, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false) { const { title: name, artist: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name: artistName, } = {}, duration, @@ -47,6 +54,7 @@ export default class DeezerSource extends AbstractSource { id, link, album: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message title: albumName, } = {}, } = obj; @@ -73,13 +81,17 @@ export default class DeezerSource extends AbstractSource { initialize = async () => { try { const credFile = await readJson(this.workingCredsPath, {throwOnNotFound: false}); + // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message this.config.accessToken = credFile.accessToken; } catch (e) { this.logger.warn('Current deezer credentials file exists but could not be parsed', { path: this.workingCredsPath }); } + // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message if(this.config.accessToken === undefined) { + // @ts-expect-error TS(2339): Property 'clientId' does not exist on type '{}'. if(this.config.clientId === undefined) { throw new Error('clientId must be defined when accessToken is not present'); + // @ts-expect-error TS(2339): Property 'clientSecret' does not exist on type '{}... Remove this comment to see the full error message } else if(this.config.clientSecret === undefined) { throw new Error('clientSecret must be defined when accessToken is not present'); } @@ -100,21 +112,26 @@ export default class DeezerSource extends AbstractSource { } getRecentlyPlayed = async (options = {}) => { + // @ts-expect-error TS(2339): Property 'formatted' does not exist on type '{}'. const {formatted = false} = options; const resp = await this.callApi(request.get(`${this.baseUrl}/user/me/history`)); if(formatted) { - return resp.data.map(x => DeezerSource.formatPlayObj(x)).sort(sortByPlayDate) + return resp.data.map((x: any) => DeezerSource.formatPlayObj(x)).sort(sortByPlayDate); } return resp.data; } - callApi = async (req, retries = 0) => { + // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message + callApi = async (req: any, retries = 0) => { const { + // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 1, + // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5 } = this.config; req.query({ + // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message access_token: this.config.accessToken, output: 'json' }); @@ -123,13 +140,17 @@ export default class DeezerSource extends AbstractSource { const { body = {}, body: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message error, } = {} } = resp; if (error !== undefined) { const err = new Error(error.message); + // @ts-expect-error TS(2339): Property 'type' does not exist on type 'Error'. err.type = error.type; + // @ts-expect-error TS(2339): Property 'code' does not exist on type 'Error'. err.code = error.code; + // @ts-expect-error TS(2339): Property 'response' does not exist on type 'Error'... Remove this comment to see the full error message err.response = resp; throw err; } @@ -142,21 +163,30 @@ export default class DeezerSource extends AbstractSource { return await this.callApi(req, retries + 1) } const { + // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message message, + // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status, body: { "subsonic-response": { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status: ssStatus, error: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message code, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message message: ssMessage, } = {}, } = {}, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message "subsonic-response": ssResp } = {}, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message text, } = {}, + // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response, } = e; let msg = response !== undefined ? `API Call failed: Server Response => ${ssMessage}` : `API Call failed: ${message}`; @@ -168,11 +198,14 @@ export default class DeezerSource extends AbstractSource { generatePassportStrategy = () => { return new DeezerStrategy({ + // @ts-expect-error TS(2339): Property 'clientId' does not exist on type '{}'. clientID: this.config.clientId, + // @ts-expect-error TS(2339): Property 'clientSecret' does not exist on type '{}... Remove this comment to see the full error message clientSecret: this.config.clientSecret, + // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{}'... Remove this comment to see the full error message callbackURL: this.config.redirectUri || `${this.localUrl}/deezer/callback`, scope: ['listening_history','offline_access'], - }, (accessToken, refreshToken, profile, done) => { + }, (accessToken: any, refreshToken: any, profile: any, done: any) => { // return done(null, { // accessToken, // refreshToken, @@ -191,7 +224,7 @@ export default class DeezerSource extends AbstractSource { }); } - handleAuthCodeCallback = async (res) => { + handleAuthCodeCallback = async (res: any) => { const {error, accessToken, id, displayName} = res; if (error === undefined) { await writeFile(this.workingCredsPath, JSON.stringify({ @@ -199,6 +232,7 @@ export default class DeezerSource extends AbstractSource { id, displayName, })); + // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message this.config.accessToken = accessToken; this.logger.info('Got token Deezer SDK callback!'); return true; diff --git a/src/sources/JellyfinSource.ts b/src/sources/JellyfinSource.ts index f72d7300..f54c2614 100644 --- a/src/sources/JellyfinSource.ts +++ b/src/sources/JellyfinSource.ts @@ -4,12 +4,14 @@ import {buildTrackString, parseDurationFromTimestamp} from "../utils.js"; export default class JellyfinSource extends MemorySource { + // @ts-expect-error TS(7022): 'users' implicitly has type 'any' because it does ... Remove this comment to see the full error message users; + // @ts-expect-error TS(7022): 'servers' implicitly has type 'any' because it doe... Remove this comment to see the full error message servers; seenServers = {}; - constructor(name, config, clients, type = 'jellyfin') { + constructor(name: any, config: any, clients: any, type = 'jellyfin') { super(type, name, config, clients); const {users, servers} = config @@ -21,7 +23,7 @@ export default class JellyfinSource extends MemorySource { } else { this.users = users; } - this.users = this.users.map(x => x.toLocaleLowerCase()) + this.users = this.users.map((x: any) => x.toLocaleLowerCase()) } if (servers === undefined || servers === null) { @@ -32,7 +34,7 @@ export default class JellyfinSource extends MemorySource { } else { this.servers = servers; } - this.servers = this.servers.map(x => x.toLocaleLowerCase()) + this.servers = this.servers.map((x: any) => x.toLocaleLowerCase()) } if (users === undefined && servers === undefined) { @@ -43,7 +45,7 @@ export default class JellyfinSource extends MemorySource { this.initialized = true; } - static formatPlayObj(obj, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false) { const { ServerId, ServerName, @@ -85,13 +87,15 @@ export default class JellyfinSource extends MemorySource { } } - isValidEvent = (playObj) => { + isValidEvent = (playObj: any) => { const { meta: { mediaType, event, user, server }, data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {} } = playObj; @@ -139,8 +143,10 @@ export default class JellyfinSource extends MemorySource { return this.statefulRecentlyPlayed; } - handle = async (playObj, allClients) => { + handle = async (playObj: any, allClients: any) => { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message if(this.seenServers[playObj.meta.server] === undefined) { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message this.seenServers[playObj.meta.server] = playObj.meta.sourceVersion; this.logger.info(`Received data from server ${playObj.meta.server} (Version ${playObj.meta.sourceVersion}) for the first time.`); } @@ -158,6 +164,7 @@ export default class JellyfinSource extends MemorySource { const recent = await this.getRecentlyPlayed(); const newestPlay = recent[recent.length - 1]; try { + // @ts-expect-error TS(2339): Property 'data' does not exist on type 'never'. await allClients.scrobble(newPlays, {scrobbleTo: this.clients, scrobbleFrom: this.identifier, checkTime: newestPlay.data.playDate}); // only gets hit if we scrobbled ok this.tracksDiscovered++; diff --git a/src/sources/LastfmSource.ts b/src/sources/LastfmSource.ts index 8c50f279..f06259c5 100644 --- a/src/sources/LastfmSource.ts +++ b/src/sources/LastfmSource.ts @@ -8,13 +8,13 @@ export default class LastfmSource extends AbstractSource { requiresAuth = true; requiresAuthInteraction = true; - constructor(name, config = {}, clients = []) { + constructor(name: any, config = {}, clients = []) { super('lastfm', name, config, clients); this.canPoll = true; this.api = new LastfmApiClient(name, config); } - static formatPlayObj(obj) { + static formatPlayObj(obj: any) { return LastfmApiClient.formatPlayObj(obj); } @@ -36,15 +36,16 @@ export default class LastfmSource extends AbstractSource { getRecentlyPlayed = async(options = {}) => { + // @ts-expect-error TS(2339): Property 'limit' does not exist on type '{}'. const {limit = 20, formatted = false} = options; - const resp = await this.api.callApi(client => client.userGetRecentTracks({user: this.api.user, limit, extended: true})); + const resp = await this.api.callApi((client: any) => client.userGetRecentTracks({user: this.api.user, limit, extended: true})); const { recenttracks: { track: list = [], } } = resp; - return list.reduce((acc, x) => { + return list.reduce((acc: any, x: any) => { try { const formatted = LastfmApiClient.formatPlayObj(x); const { @@ -69,6 +70,7 @@ export default class LastfmSource extends AbstractSource { } return acc.concat(formatted); } catch (e) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. this.logger.warn('Failed to format Last.fm recently scrobbled track, omitting from time frame check', {error: e.message}); this.logger.debug('Full api response object:'); this.logger.debug(x); diff --git a/src/sources/MemorySource.ts b/src/sources/MemorySource.ts index 5100a8bf..9b5368e8 100644 --- a/src/sources/MemorySource.ts +++ b/src/sources/MemorySource.ts @@ -14,11 +14,11 @@ export default class MemorySource extends AbstractSource { statefulRecentlyPlayed = []; candidateRecentlyPlayed = []; - processRecentPlays = (plays) => { + processRecentPlays = (plays: any) => { - let newStatefulPlays = []; + let newStatefulPlays: any = []; // first format new plays with locked play date - const lockedPlays = plays.map((p) => { + const lockedPlays = plays.map((p: any) => { const {data: {playDate, ...restData}, ...rest} = p; return {data: {...restData, playDate: dayjs()}, ...rest}; }) @@ -30,7 +30,7 @@ export default class MemorySource extends AbstractSource { this.candidateRecentlyPlayed = lockedPlays; } else { // otherwise determine new tracks (not found in prior candidates) - const newTracks = lockedPlays.filter(x => this.candidateRecentlyPlayed.every(y => !playObjDataMatch(y, x))); + const newTracks = lockedPlays.filter((x: any) => this.candidateRecentlyPlayed.every(y => !playObjDataMatch(y, x))); if(newTracks.length > 0) { for(const p of newTracks) { this.logger.debug(`New play found that does not match existing candidates will be added: ${buildTrackString(p, {include: ['sourceId', 'artist', 'track']})}`); @@ -38,7 +38,7 @@ export default class MemorySource extends AbstractSource { } // filter prior candidates based on new recently played this.candidateRecentlyPlayed = this.candidateRecentlyPlayed.filter(x => { - const candidateMatchedLocked = lockedPlays.some(y => playObjDataMatch(x, y)); + const candidateMatchedLocked = lockedPlays.some((y: any) => playObjDataMatch(x, y)); if(!candidateMatchedLocked) { this.logger.debug(`Existing candidate not found in locked plays will be removed: ${buildTrackString(x, {include: ['sourceId', 'artist', 'track']})}`); } @@ -50,6 +50,7 @@ export default class MemorySource extends AbstractSource { for(const candidate of this.candidateRecentlyPlayed) { const {data: {playDate, track}} = candidate; + // @ts-expect-error TS(2339): Property 'isBefore' does not exist on type 'never'... Remove this comment to see the full error message if(playDate.isBefore(dayjs().subtract(30, 's'))) { // a prior candidate has been playing for more than 30 seconds, time to check statefuls @@ -62,8 +63,10 @@ export default class MemorySource extends AbstractSource { } else { const {data: { playDate, duration }} = candidate; const {data: { playDate: rplayDate }} = matchingRecent; + // @ts-expect-error TS(2339): Property 'isSame' does not exist on type 'never'. if(!playDate.isSame(rplayDate)) { if(duration !== undefined) { + // @ts-expect-error TS(2339): Property 'isAfter' does not exist on type 'never'. if(playDate.isAfter(rplayDate.add(duration, 's'))) { this.logger.debug(`${stPrefix} added after being seen for 30 seconds and having a different timestamp than a prior play`); newStatefulPlays.push(candidate); @@ -84,7 +87,7 @@ export default class MemorySource extends AbstractSource { return newStatefulPlays; } - recentlyPlayedTrackIsValid = (playObj) => { + recentlyPlayedTrackIsValid = (playObj: any) => { return playObj.data.playDate.isBefore(dayjs().subtract(30, 's')); } } diff --git a/src/sources/PlexSource.ts b/src/sources/PlexSource.ts index 6b9133a3..190157be 100644 --- a/src/sources/PlexSource.ts +++ b/src/sources/PlexSource.ts @@ -1,16 +1,22 @@ +// @ts-expect-error TS(7016): Could not find a declaration file for module 'last... Remove this comment to see the full error message import dayjs from "dayjs";import LastFm from "lastfm-node-client"; import LastfmScrobbler from '../clients/LastfmScrobbler.js'; import {buildTrackString, createLabelledLogger} from "../utils.js"; import AbstractSource from "./AbstractSource.js"; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'form... Remove this comment to see the full error message import formidable from 'formidable'; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'conc... Remove this comment to see the full error message import concatStream from 'concat-stream'; export default class PlexSource extends AbstractSource { + // @ts-expect-error TS(7022): 'users' implicitly has type 'any' because it does ... Remove this comment to see the full error message users; + // @ts-expect-error TS(7022): 'libraries' implicitly has type 'any' because it d... Remove this comment to see the full error message libraries; + // @ts-expect-error TS(7022): 'servers' implicitly has type 'any' because it doe... Remove this comment to see the full error message servers; - constructor(name, config, clients, type = 'plex') { + constructor(name: any, config: any, clients: any, type = 'plex') { super(type, name, config, clients); const {user, libraries, servers} = config @@ -22,7 +28,7 @@ export default class PlexSource extends AbstractSource { } else { this.users = user; } - this.users = this.users.map(x => x.toLocaleLowerCase()) + this.users = this.users.map((x: any) => x.toLocaleLowerCase()) } if (libraries === undefined || libraries === null) { @@ -33,7 +39,7 @@ export default class PlexSource extends AbstractSource { } else { this.libraries = libraries; } - this.libraries = this.libraries.map(x => x.toLocaleLowerCase()) + this.libraries = this.libraries.map((x: any) => x.toLocaleLowerCase()) } if (servers === undefined || servers === null) { @@ -44,7 +50,7 @@ export default class PlexSource extends AbstractSource { } else { this.servers = servers; } - this.servers = this.servers.map(x => x.toLocaleLowerCase()) + this.servers = this.servers.map((x: any) => x.toLocaleLowerCase()) } if (user === undefined && libraries === undefined && servers === undefined) { @@ -55,20 +61,27 @@ export default class PlexSource extends AbstractSource { this.initialized = true; } - static formatPlayObj(obj, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false) { const { event, Account: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message title: user, } = {}, Metadata: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message type, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message title: track, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message parentTitle: album, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message grandparentTitle: artist, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message librarySectionTitle: library } = {}, Server: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message title: server } = {}, } = obj; @@ -91,13 +104,15 @@ export default class PlexSource extends AbstractSource { } } - isValidEvent = (playObj) => { + isValidEvent = (playObj: any) => { const { meta: { mediaType, event, user, library, server }, data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {} } = playObj; @@ -159,7 +174,7 @@ export default class PlexSource extends AbstractSource { return true; } - handle = async (playObj, allClients) => { + handle = async (playObj: any, allClients: any) => { if (!this.isValidEvent(playObj)) { return; } @@ -180,21 +195,21 @@ export const plexRequestMiddle = () => { const plexLog = createLabelledLogger('plexReq', 'Plex Request'); - return async (req, res, next) => { + return async (req: any, res: any, next: any) => { const form = formidable({ allowEmptyFiles: true, multiples: true, - fileWriteStreamHandler: (file) => { - return concatStream((data) => { + fileWriteStreamHandler: (file: any) => { + return concatStream((data: any) => { file.buffer = data; - }) + }); } }); - form.on('progress', (received, expected) => { + form.on('progress', (received: any, expected: any) => { plexLog.debug(`Received ${received} bytes of expected ${expected}`); }); - form.on('error', (err) => { + form.on('error', (err: any) => { plexLog.error(err); }) form.on('aborted', () => { @@ -203,10 +218,10 @@ export const plexRequestMiddle = () => { form.on('end', () => { plexLog.debug('Received end of form data from Plex'); }); - form.on('fileBegin', (formname, file) => { + form.on('fileBegin', (formname: any, file: any) => { plexLog.debug(`File Begin: ${formname}`); }); - form.on('file', (formname,) => { + form.on('file', (formname: any) => { plexLog.debug(`File Recieved: ${formname}`); }); @@ -214,7 +229,7 @@ export const plexRequestMiddle = () => { plexLog.debug('Receiving request from Plex...'); return new Promise((resolve, reject) => { - form.parse(req, (err, fields, files) => { + form.parse(req, (err: any, fields: any, files: any) => { if (err) { plexLog.error('Error occurred while parsing formdata'); plexLog.error(err); @@ -225,6 +240,7 @@ export const plexRequestMiddle = () => { let validFile = null; for (const namedFiles of Object.values(files)) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. for(const file of namedFiles) { if (file.mimetype.includes('json')) { validFile = file; @@ -233,6 +249,7 @@ export const plexRequestMiddle = () => { } } if (validFile === null) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. const err = new Error(`No files parsed from formdata had a mimetype that included 'json'. Found files:\n ${Object.entries(files).map(([k, v]) => `${k}: ${v.mimetype}`).join('\n')}`); plexLog.error(err); next(err); @@ -246,6 +263,7 @@ export const plexRequestMiddle = () => { payload = JSON.parse(payloadRaw); req.payload = payload; next(); + // @ts-expect-error TS(2794): Expected 1 arguments, but got 0. Did you forget to... Remove this comment to see the full error message resolve(); } catch (e) { plexLog.error(`Error occurred while trying to parse Plex file payload to json. Raw text:\n${payloadRaw}`); @@ -255,5 +273,5 @@ export const plexRequestMiddle = () => { } }); }); - } + }; } diff --git a/src/sources/ScrobbleSources.ts b/src/sources/ScrobbleSources.ts index 73f28771..8f009062 100644 --- a/src/sources/ScrobbleSources.ts +++ b/src/sources/ScrobbleSources.ts @@ -16,21 +16,24 @@ export default class ScrobbleSources { sourceTypes = ['spotify', 'plex', 'tautulli', 'subsonic', 'jellyfin', 'lastfm', 'deezer']; - constructor(localUrl, configDir = process.cwd()) { + constructor(localUrl: any, configDir = process.cwd()) { this.configDir = configDir; this.localUrl = localUrl; this.logger = createLabelledLogger('sources', 'Sources'); } - getByName = (name) => { + getByName = (name: any) => { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. return this.sources.find(x => x.name === name); } - getByType = (type) => { + getByType = (type: any) => { + // @ts-expect-error TS(2339): Property 'type' does not exist on type 'never'. return this.sources.filter(x => x.type === type); } - getByNameAndType = (name, type) => { + getByNameAndType = (name: any, type: any) => { + // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. return this.sources.find(x => x.name === name && x.type === type); } @@ -50,7 +53,7 @@ export default class ScrobbleSources { sourceDefaults: sd = {}, } = configFile; sourceDefaults = sd; - const validMainConfigs = mainConfigSourcesConfigs.reduce((acc, curr, i) => { + const validMainConfigs = mainConfigSourcesConfigs.reduce((acc: any, curr: any, i: any) => { if(curr === null) { this.logger.error(`The source config entry at index ${i} in config.json is null but should be an object, will not parse`); return acc; @@ -64,8 +67,11 @@ export default class ScrobbleSources { for (const c of validMainConfigs) { const {name = 'unnamed'} = c; configs.push({...c, + // @ts-expect-error TS(2322): Type 'any' is not assignable to type 'never'. name, + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'config.json', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. configureAs: 'source' // override user value }); } @@ -85,10 +91,15 @@ export default class ScrobbleSources { }; if (!Object.values(s).every(x => x === undefined)) { configs.push({ + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'spotify', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', + // @ts-expect-error TS(2322): Type '{ accessToken: string | undefined; clientId:... Remove this comment to see the full error message data: s }) } @@ -100,10 +111,15 @@ export default class ScrobbleSources { }; if (!Object.values(t).every(x => x === undefined)) { configs.push({ + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'tautulli', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', + // @ts-expect-error TS(2322): Type '{ user: string | undefined; }' is not assign... Remove this comment to see the full error message data: t }) } @@ -114,10 +130,15 @@ export default class ScrobbleSources { }; if (!Object.values(p).every(x => x === undefined)) { configs.push({ + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'plex', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', + // @ts-expect-error TS(2322): Type '{ user: string | undefined; }' is not assign... Remove this comment to see the full error message data: p }) } @@ -130,10 +151,15 @@ export default class ScrobbleSources { }; if (!Object.values(sub).every(x => x === undefined)) { configs.push({ + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'subsonic', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', + // @ts-expect-error TS(2322): Type '{ user: string | undefined; password: string... Remove this comment to see the full error message data: sub }) } @@ -145,10 +171,15 @@ export default class ScrobbleSources { }; if (!Object.values(j).every(x => x === undefined)) { configs.push({ + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'jellyfin', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', + // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', + // @ts-expect-error TS(2322): Type '{ user: string | undefined; server: string |... Remove this comment to see the full error message data: j }) } @@ -207,6 +238,7 @@ export default class ScrobbleSources { if(configureAs === 'source') { m.source = `${sourceType}.json`; m.type = sourceType; + // @ts-expect-error TS(2345): Argument of type 'any' is not assignable to parame... Remove this comment to see the full error message configs.push(m); } } @@ -217,6 +249,7 @@ export default class ScrobbleSources { const validConfigs = configs.reduce((acc, c) => { const isValid = isValidConfigStructure(c, {type: true, data: true}); if (isValid !== true) { + // @ts-expect-error TS(2339): Property 'source' does not exist on type 'never'. this.logger.error(`Source config from ${c.source} with name [${c.name || 'unnamed'}] of type [${c.type || 'unknown'}] will not be used because it has structural errors: ${isValid.join(' | ')}`); return acc; } @@ -232,25 +265,30 @@ export default class ScrobbleSources { }, {}); // only need to warn if dup names PER TYPE for (const [type, typedConfigs] of Object.entries(typeGroupedConfigs)) { - const nameGroupedConfigs = typedConfigs.reduce((acc, curr) => { + // @ts-expect-error TS(2571): Object is of type 'unknown'. + const nameGroupedConfigs = typedConfigs.reduce((acc: any, curr: any) => { const {name = 'unnamed'} = curr; const {[name]: n = []} = acc; return {...acc, [name]: [...n, curr]}; }, {}); for (const [name, namedConfigs] of Object.entries(nameGroupedConfigs)) { let tempNamedConfigs = namedConfigs; + // @ts-expect-error TS(2571): Object is of type 'unknown'. const hasDups = namedConfigs.length > 1; if (hasDups) { - const sources = namedConfigs.map(c => `Config object from ${c.source} of type [${c.type}]`); + // @ts-expect-error TS(2571): Object is of type 'unknown'. + const sources = namedConfigs.map((c: any) => `Config object from ${c.source} of type [${c.type}]`); this.logger.warn(`Source configs have naming conflicts -- the following configs have the same name "${name}":\n\n${sources.join('\n')}\n`); if (name === 'unnamed') { this.logger.info('HINT: "unnamed" configs occur when using ENVs, if a multi-user mode config does not have a "name" property, or if a config is built in single-user mode'); } } + // @ts-expect-error TS(2571): Object is of type 'unknown'. tempNamedConfigs = tempNamedConfigs.map(({name = 'unnamed', ...x}, i) => ({ ...x, name: hasDups ? `${name}${i + 1}` : name })); + // @ts-expect-error TS(2571): Object is of type 'unknown'. for (const c of tempNamedConfigs) { try { await this.addSource(c, sourceDefaults); @@ -263,7 +301,7 @@ export default class ScrobbleSources { } } - addSource = async (clientConfig, defaults = {}) => { + addSource = async (clientConfig: any, defaults = {}) => { const isValidConfig = isValidConfigStructure(clientConfig, {name: true, data: true, type: true}); if (isValidConfig !== true) { throw new Error(`Config object from ${clientConfig.source || 'unknown'} with name [${clientConfig.name || 'unnamed'}] of type [${clientConfig.type || 'unknown'}] has errors: ${isValidConfig.join(' | ')}`) @@ -313,7 +351,7 @@ export default class ScrobbleSources { } if(newSource.initialized === false) { this.logger.debug(`(${name}) Attempting ${type} initialization...`); - if (await newSource.initialize() === false) { + if ((await newSource.initialize()) === false) { this.logger.error(`(${name}) ${type} source failed to initialize. Source needs to be successfully initialized before activity capture can begin.`); return; } else { @@ -338,6 +376,7 @@ export default class ScrobbleSources { } } + // @ts-expect-error TS(2345): Argument of type 'SpotifySource | PlexSource | Sub... Remove this comment to see the full error message this.sources.push(newSource); } } diff --git a/src/sources/SpotifySource.ts b/src/sources/SpotifySource.ts index 1b97ddc5..de850a34 100644 --- a/src/sources/SpotifySource.ts +++ b/src/sources/SpotifySource.ts @@ -4,6 +4,7 @@ import { writeFile, sortByPlayDate, sleep, parseRetryAfterSecsFromObj, } from "../utils.js"; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'spot... Remove this comment to see the full error message import SpotifyWebApi from "spotify-web-api-node"; import AbstractSource from "./AbstractSource.js"; @@ -12,7 +13,7 @@ const state = 'random'; export default class SpotifySource extends AbstractSource { - spotifyApi; + spotifyApi: any; localUrl; workingCredsPath; configDir; @@ -20,11 +21,14 @@ export default class SpotifySource extends AbstractSource { requiresAuth = true; requiresAuthInteraction = true; - constructor(name, config = {}, clients = []) { + constructor(name: any, config = {}, clients = []) { super('spotify', name, config, clients); const { + // @ts-expect-error TS(2339): Property 'localUrl' does not exist on type '{}'. localUrl, + // @ts-expect-error TS(2339): Property 'configDir' does not exist on type '{}'. configDir, + // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. interval = 60, } = config; @@ -32,6 +36,7 @@ export default class SpotifySource extends AbstractSource { this.logger.warn('Interval should be above 30 seconds...😬'); } + // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. this.config.interval = interval; this.configDir = configDir; @@ -40,17 +45,22 @@ export default class SpotifySource extends AbstractSource { this.canPoll = true; } - static formatPlayObj(obj, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false) { const { track: { artists = [], + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message id, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message duration_ms, album: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name: albumName, } = {}, external_urls: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message spotify, } = {} } = {}, @@ -59,7 +69,7 @@ export default class SpotifySource extends AbstractSource { //let artistString = artists.reduce((acc, curr) => acc.concat(curr.name), []).join(','); return { data: { - artists: artists.map(x => x.name), + artists: artists.map((x: any) => x.name), album: albumName, track: name, duration: duration_ms / 1000, @@ -74,7 +84,7 @@ export default class SpotifySource extends AbstractSource { web: spotify } } - } + }; } buildSpotifyApi = async () => { @@ -87,16 +97,22 @@ export default class SpotifySource extends AbstractSource { } const { + // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message accessToken, + // @ts-expect-error TS(2339): Property 'clientId' does not exist on type '{}'. clientId, + // @ts-expect-error TS(2339): Property 'clientSecret' does not exist on type '{}... Remove this comment to see the full error message clientSecret, + // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{}'... Remove this comment to see the full error message redirectUri, + // @ts-expect-error TS(2339): Property 'refreshToken' does not exist on type '{}... Remove this comment to see the full error message refreshToken, } = this.config || {}; const rdUri = redirectUri || `${this.localUrl}/callback`; + // @ts-expect-error TS(2339): Property 'token' does not exist on type '{}'. const {token = accessToken, refreshToken: rt = refreshToken} = spotifyCreds || {}; const apiConfig = { @@ -110,6 +126,7 @@ export default class SpotifySource extends AbstractSource { this.logger.info('No values found for Spotify configuration, skipping initialization'); return; } + // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{ c... Remove this comment to see the full error message apiConfig.redirectUri = rdUri; const validationErrors = []; @@ -153,7 +170,7 @@ export default class SpotifySource extends AbstractSource { testAuth = async () => { try { - await this.callApi((api => api.getMe())); + await this.callApi(((api: any) => api.getMe())); this.authed = true; } catch (e) { this.logger.error('Could not successfully communicate with Spotify API'); @@ -166,7 +183,10 @@ export default class SpotifySource extends AbstractSource { return this.spotifyApi.createAuthorizeURL(scopes, this.name); } - handleAuthCodeCallback = async ({error, code}) => { + handleAuthCodeCallback = async ({ + error, + code + }: any) => { if (error === undefined) { const tokenResponse = await this.spotifyApi.authorizationCodeGrant(code); this.spotifyApi.setAccessToken(tokenResponse.body['access_token']); @@ -185,25 +205,30 @@ export default class SpotifySource extends AbstractSource { } getRecentlyPlayed = async (options = {}) => { + // @ts-expect-error TS(2339): Property 'limit' does not exist on type '{}'. const {limit = 20, formatted = false} = options; - const func = api => api.getMyRecentlyPlayedTracks({ + const func = (api: any) => api.getMyRecentlyPlayedTracks({ limit }); const result = await this.callApi(func); if (formatted === true) { - return result.body.items.map(x => SpotifySource.formatPlayObj(x)).sort(sortByPlayDate); + return result.body.items.map((x: any) => SpotifySource.formatPlayObj(x)).sort(sortByPlayDate); } return result; } - callApi = async (func, retries = 0) => { + // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message + callApi = async (func: any, retries = 0) => { const { + // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 1, + // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 2, } = this.config; try { return await func(this.spotifyApi); } catch (e) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. if (e.statusCode === 401) { if (this.spotifyApi.getRefreshToken() === undefined) { throw new Error('Access token was not valid and no refresh token was present') @@ -213,6 +238,7 @@ export default class SpotifySource extends AbstractSource { const tokenResponse = await this.spotifyApi.refreshAccessToken(); const { body: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message access_token, // spotify may return a new refresh token // if it doesn't then continue to use the last refresh token we received @@ -228,6 +254,7 @@ export default class SpotifySource extends AbstractSource { return await func(this.spotifyApi); } catch (ee) { this.logger.error('Refreshing access token encountered an error'); + // @ts-expect-error TS(2769): No overload matches this call. this.logger.error(ee, {label: 'Spotify'}); throw ee; } @@ -238,13 +265,14 @@ export default class SpotifySource extends AbstractSource { return this.callApi(func, retries + 1); } else { this.logger.error(`Request failed on retry (${retries}) with no more retries permitted (max ${maxRequestRetries})`); + // @ts-expect-error TS(2769): No overload matches this call. this.logger.error(e, {label: 'Spotify'}); throw e; } } } - poll = async (allClients) => { + poll = async (allClients: any) => { if (this.spotifyApi === undefined) { this.logger.warn('Cannot poll spotify without valid credentials configuration') return; diff --git a/src/sources/SubsonicSource.ts b/src/sources/SubsonicSource.ts index fc6ee8d1..d5d822a6 100644 --- a/src/sources/SubsonicSource.ts +++ b/src/sources/SubsonicSource.ts @@ -1,4 +1,5 @@ import AbstractSource from "./AbstractSource.js"; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'supe... Remove this comment to see the full error message import request from 'superagent'; import crypto from 'crypto'; import dayjs from "dayjs"; @@ -12,11 +13,12 @@ export class SubsonicSource extends MemorySource { requiresAuth = true; - constructor(name, config = {}, clients = []) { + constructor(name: any, config = {}, clients = []) { // default to quick interval so we can get a decently accurate nowPlaying const subsonicConfig = {interval: 10, maxSleep: 30, ...config}; super('subsonic', name, subsonicConfig, clients); + // @ts-expect-error TS(2339): Property 'user' does not exist on type '{}'. const {user, password, url} = this.config; if (user === undefined) { @@ -32,7 +34,7 @@ export class SubsonicSource extends MemorySource { this.canPoll = true; } - static formatPlayObj(obj, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false) { const { id, title, @@ -60,11 +62,16 @@ export class SubsonicSource extends MemorySource { } } - callApi = async (req, retries = 0) => { + // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message + callApi = async (req: any, retries = 0) => { const { + // @ts-expect-error TS(2339): Property 'user' does not exist on type '{}'. user, + // @ts-expect-error TS(2339): Property 'password' does not exist on type '{}'. password, + // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 1, + // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5 } = this.config; @@ -90,6 +97,7 @@ export class SubsonicSource extends MemorySource { } = resp; if (status === 'failed') { const err = new Error('Subsonic API returned an error'); + // @ts-expect-error TS(2339): Property 'response' does not exist on type 'Error'... Remove this comment to see the full error message err.response = resp; throw err; } @@ -102,21 +110,30 @@ export class SubsonicSource extends MemorySource { return await this.callApi(req, retries + 1) } const { + // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message message, + // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status, body: { "subsonic-response": { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status: ssStatus, error: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message code, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message message: ssMessage, } = {}, } = {}, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message "subsonic-response": ssResp } = {}, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message text, } = {}, + // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response, } = e; let msg = response !== undefined ? `API Call failed: Server Response => ${ssMessage}` : `API Call failed: ${message}`; @@ -127,12 +144,14 @@ export class SubsonicSource extends MemorySource { } initialize = async () => { + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url} = this.config; try { await request.get(`${url}/`); this.logger.info('Subsonic Connection: ok'); this.initialized = true; } catch (e) { + // @ts-expect-error TS(2571): Object is of type 'unknown'. if(e.status !== undefined && e.status !== 404) { this.logger.info('Subsonic Connection: ok'); // we at least got a response! @@ -144,6 +163,7 @@ export class SubsonicSource extends MemorySource { } testAuth= async () => { + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url} = this.config; try { await this.callApi(request.get(`${url}/rest/ping`)); @@ -157,7 +177,9 @@ export class SubsonicSource extends MemorySource { } getRecentlyPlayed = async (options = {}) => { + // @ts-expect-error TS(2339): Property 'formatted' does not exist on type '{}'. const {formatted = false} = options; + // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. const {url} = this.config; const resp = await this.callApi(request.get(`${url}/rest/getNowPlaying`)); const { @@ -165,7 +187,7 @@ export class SubsonicSource extends MemorySource { entry = [] } = {} } = resp; - this.processRecentPlays(entry.map(x => formatted ? SubsonicSource.formatPlayObj(x) : x)); + this.processRecentPlays(entry.map((x: any) => formatted ? SubsonicSource.formatPlayObj(x) : x)); return this.statefulRecentlyPlayed; } } diff --git a/src/sources/TautulliSource.ts b/src/sources/TautulliSource.ts index 79e4ca84..71c7fb50 100644 --- a/src/sources/TautulliSource.ts +++ b/src/sources/TautulliSource.ts @@ -1,13 +1,14 @@ import dayjs from "dayjs"; import PlexSource from "./PlexSource.js"; +// @ts-expect-error TS(2417): Class static side 'typeof TautulliSource' incorrec... Remove this comment to see the full error message export default class TautulliSource extends PlexSource { - constructor(name, config, clients) { + constructor(name: any, config: any, clients: any) { super(name, config, clients, 'tautulli'); } - static formatPlayObj(obj, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false) { const { artist_name, track_name, diff --git a/src/utils.ts b/src/utils.ts index 450f36e0..f24f21bb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,7 +3,9 @@ import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc.js'; import winston from "winston"; import jsonStringify from 'safe-stable-stringify'; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'spot... Remove this comment to see the full error message import { TimeoutError, WebapiError } from "spotify-web-api-node/src/response-error.js"; +// @ts-expect-error TS(7016): Could not find a declaration file for module 'supe... Remove this comment to see the full error message import { Response } from 'superagent'; const {format} = winston; @@ -11,12 +13,14 @@ const {combine, printf, timestamp, label, splat, errors} = format; dayjs.extend(utc); -export async function readJson(path, {logErrors = true, throwOnNotFound = true} = {}) { +export async function readJson(this: any, path: any, {logErrors = true, throwOnNotFound = true} = {}) { try { await promises.access(path, constants.R_OK); const data = await promises.readFile(path); + // @ts-expect-error TS(2345): Argument of type 'Buffer' is not assignable to par... Remove this comment to see the full error message return JSON.parse(data); } catch (e) { + // @ts-expect-error TS(2339): Property 'code' does not exist on type 'unknown'. const {code} = e; if (code === 'ENOENT') { if (throwOnNotFound) { @@ -35,7 +39,7 @@ export async function readJson(path, {logErrors = true, throwOnNotFound = true} } } -export async function readText(path) { +export async function readText(path: any) { await promises.access(path, constants.R_OK); const data = await promises.readFile(path); return data.toString(); @@ -50,7 +54,7 @@ export async function readText(path) { // }); } -export async function writeFile(path, text) { +export async function writeFile(path: any, text: any) { // await promises.access(path, constants.W_OK | constants.O_CREAT); await promises.writeFile(path, text, 'utf8'); @@ -65,37 +69,44 @@ export async function writeFile(path, text) { } -export function sleep(ms) { +export function sleep(ms: any) { return new Promise(resolve => setTimeout(resolve, ms)); } -export const longestString = strings => strings.reduce((acc, curr) => curr.length > acc ? curr.length : acc, 0); -export const truncateStringArrToLength = (length, truncStr = '...') => { +export const longestString = (strings: any) => strings.reduce((acc: any, curr: any) => curr.length > acc ? curr.length : acc, 0); +export const truncateStringArrToLength = (length: any, truncStr = '...') => { const truncater = truncateStringToLength(length, truncStr); - return strings => strings.map(truncater); + return (strings: any) => strings.map(truncater); } -export const truncateStringToLength = (length, truncStr = '...') => str => str.length > length ? `${str.slice(0, length)}${truncStr}` : str; +export const truncateStringToLength = (length: any, truncStr = '...') => (str: any) => str.length > length ? `${str.slice(0, length)}${truncStr}` : str; -const defaultTransformer = input => input; +const defaultTransformer = (input: any) => input; -export const buildTrackString = (playObj, options = {}) => { +export const buildTrackString = (playObj: any, options = {}) => { const { + // @ts-expect-error TS(2339): Property 'include' does not exist on type '{}'. include = ['time', 'artist', 'track'], + // @ts-expect-error TS(2339): Property 'transformers' does not exist on type '{}... Remove this comment to see the full error message transformers: { - artists: artistsFunc = a => a.join(' / '), + artists: artistsFunc = (a: any) => a.join(' / '), track: trackFunc = defaultTransformer, - time: timeFunc = t => t.local().format(), - timeFromNow = t => t.local().fromNow(), + time: timeFunc = (t: any) => t.local().format(), + timeFromNow = (t: any) => t.local().fromNow(), } = {} } = options; const { data: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message sourceId } = {}, } = playObj; @@ -124,7 +135,7 @@ export const buildTrackString = (playObj, options = {}) => { } // sorts playObj formatted objects by playDate in ascending (oldest first) order -export const sortByPlayDate = (a, b) => a.data.playDate.isAfter(b.data.playDate) ? 1 : -1; +export const sortByPlayDate = (a: any, b: any) => a.data.playDate.isAfter(b.data.playDate) ? 1 : -1; const s = splat(); const SPLAT = Symbol.for('splat') @@ -144,7 +155,7 @@ export const defaultFormat = printf(({level, message, label = 'App', timestamp, msg = stackArr[0]; const cleanedStack = stackArr .slice(1) // don't need actual error message since we are showing it as msg - .map(x => x.replace(CWD, 'CWD')) // replace file location up to cwd for user privacy + .map((x: any) => x.replace(CWD, 'CWD')) // replace file location up to cwd for user privacy .join('\n'); // rejoin with newline to preserve formatting stackMsg = `\n${cleanedStack}`; } @@ -180,7 +191,7 @@ export const createLabelledLogger = (name = 'default', label = 'App') => { return winston.loggers.get(name); } -export const setIntersection = (setA, setB) => { +export const setIntersection = (setA: any, setB: any) => { let _intersection = new Set() for (let elem of setB) { if (setA.has(elem)) { @@ -190,7 +201,8 @@ export const setIntersection = (setA, setB) => { return _intersection } -export const isValidConfigStructure = (obj, required = {}) => { +export const isValidConfigStructure = (obj: any, required = {}) => { + // @ts-expect-error TS(2339): Property 'name' does not exist on type '{}'. const {name = false, type = false, data = true} = required; const errs = []; if (obj.type === undefined && type) { @@ -208,15 +220,15 @@ export const isValidConfigStructure = (obj, required = {}) => { return true; } -export const returnDuplicateStrings = (arr) => { - const alreadySeen = []; - const dupes = []; +export const returnDuplicateStrings = (arr: any) => { + const alreadySeen: any = []; + const dupes: any = []; - arr.forEach(str => alreadySeen[str] ? dupes.push(str) : alreadySeen[str] = true); + arr.forEach((str: any) => alreadySeen[str] ? dupes.push(str) : alreadySeen[str] = true); return dupes; } -export const capitalize = (str) => { +export const capitalize = (str: any) => { return str.charAt(0).toUpperCase() + str.slice(1) } /** @@ -225,15 +237,19 @@ export const capitalize = (str) => { * Checks sources and source ID's (unique identifiers) first then * Checks track, album, and artists in that order * */ -export const playObjDataMatch = (a, b) => { +export const playObjDataMatch = (a: any, b: any) => { const { data: { artists: aArtists = [], + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album: aAlbum, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track: aTrack, } = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source: aSource, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message sourceId: aSourceId, } = {}, } = a; @@ -241,11 +257,15 @@ export const playObjDataMatch = (a, b) => { const { data: { artists: bArtists = [], + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album: bAlbum, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track: bTrack, } = {}, meta: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source: bSource, + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message sourceId: bSourceId, } = {}, } = b; @@ -267,14 +287,14 @@ export const playObjDataMatch = (a, b) => { return false; } // check if every artist from either playObj matches (one way or another) with the artists from the other play obj - if (!aArtists.every(x => bArtists.includes(x)) && bArtists.every(x => aArtists.includes(x))) { + if (!aArtists.every((x: any) => bArtists.includes(x)) && bArtists.every((x: any) => aArtists.includes(x))) { return false } return true; } -export const parseRetryAfterSecsFromObj = (err) => { +export const parseRetryAfterSecsFromObj = (err: any) => { let raVal; @@ -291,6 +311,7 @@ export const parseRetryAfterSecsFromObj = (err) => { // } const { response: { + // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message headers, // returned in superagent error } = {}, retryAfter: ra // possible custom property we have set @@ -312,6 +333,7 @@ export const parseRetryAfterSecsFromObj = (err) => { return retryAfter; // got a number! } // try to parse as date + // @ts-expect-error TS(2322): Type 'Dayjs' is not assignable to type 'number'. retryAfter = dayjs(retryAfter); if (!dayjs.isDayjs(retryAfter)) { return undefined; // could not parse string if not in ISO 8601 format @@ -327,7 +349,7 @@ export const parseRetryAfterSecsFromObj = (err) => { return diff; } -export const spreadDelay = (retries, multiplier) => { +export const spreadDelay = (retries: any, multiplier: any) => { if(retries === 0) { return []; } @@ -339,14 +361,17 @@ export const spreadDelay = (retries, multiplier) => { return s; } -export const removeUndefinedKeys = (obj) => { +export const removeUndefinedKeys = (obj: any) => { let newObj = {}; Object.keys(obj).forEach((key) => { if(Array.isArray(obj[key])) { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newObj[key] = obj[key]; } else if (obj[key] === Object(obj[key])) { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newObj[key] = removeUndefinedKeys(obj[key]); } else if (obj[key] !== undefined) { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newObj[key] = obj[key]; } }); @@ -354,14 +379,16 @@ export const removeUndefinedKeys = (obj) => { return undefined; } Object.keys(newObj).forEach(key => { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message if(newObj[key] === undefined || (null !== newObj[key] && typeof newObj[key] === 'object' && Object.keys(newObj[key]).length === 0)) { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message delete newObj[key] } }); return newObj; } -export const parseDurationFromTimestamp = (timestamp) => { +export const parseDurationFromTimestamp = (timestamp: any) => { if (timestamp === null || timestamp === undefined) { return undefined; } -- 2.51.2 From b4303fa81caacca36ffc6caea4a91037e3fde2c2 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 12:22:55 -0500 Subject: [PATCH 19/64] refactor: add typings everywhere they are needed --- .gitignore | 1 + package-lock.json | 709 +++++++++--------- package.json | 19 +- src/apis/AbstractApiClient.ts | 24 +- src/apis/LastfmApiClient.ts | 37 +- src/clients/AbstractScrobbleClient.ts | 41 +- src/clients/LastfmScrobbler.ts | 26 +- src/clients/MalojaScrobbler.ts | 12 +- src/clients/ScrobbleClients.ts | 135 ++-- src/common/index.ts | 18 +- src/common/infrastructure/Atomic.ts | 60 ++ src/common/infrastructure/config/aioConfig.ts | 11 + .../infrastructure/config/client/clients.ts | 6 + .../infrastructure/config/client/index.ts | 38 + .../infrastructure/config/client/lastfm.ts | 31 + .../infrastructure/config/client/maloja.ts | 19 + src/common/infrastructure/config/common.ts | 24 + .../infrastructure/config/source/deezer.ts | 31 + .../infrastructure/config/source/index.ts | 26 + .../infrastructure/config/source/jellyfin.ts | 22 + .../infrastructure/config/source/lastfm.ts | 11 + .../infrastructure/config/source/plex.ts | 28 + .../infrastructure/config/source/sources.ts | 11 + .../infrastructure/config/source/spotify.ts | 32 + .../infrastructure/config/source/subsonic.ts | 17 + .../infrastructure/config/source/tautulli.ts | 8 + .../typings/lastfm-node-client.d.ts | 108 +++ src/common/schema/aio.json | 677 +++++++++++++++++ src/index.ts | 77 +- src/sources/AbstractSource.ts | 76 +- src/sources/DeezerSource.ts | 31 +- src/sources/JellyfinSource.ts | 16 +- src/sources/LastfmSource.ts | 21 +- src/sources/MemorySource.ts | 2 +- src/sources/PlexSource.ts | 24 +- src/sources/ScrobbleSources.ts | 217 +++--- src/sources/SpotifySource.ts | 31 +- src/sources/SubsonicSource.ts | 30 +- src/sources/TautulliSource.ts | 12 +- src/utils.ts | 75 +- tsconfig.json | 5 +- 41 files changed, 2017 insertions(+), 782 deletions(-) create mode 100644 src/common/infrastructure/Atomic.ts create mode 100644 src/common/infrastructure/config/aioConfig.ts create mode 100644 src/common/infrastructure/config/client/clients.ts create mode 100644 src/common/infrastructure/config/client/index.ts create mode 100644 src/common/infrastructure/config/client/lastfm.ts create mode 100644 src/common/infrastructure/config/client/maloja.ts create mode 100644 src/common/infrastructure/config/common.ts create mode 100644 src/common/infrastructure/config/source/deezer.ts create mode 100644 src/common/infrastructure/config/source/index.ts create mode 100644 src/common/infrastructure/config/source/jellyfin.ts create mode 100644 src/common/infrastructure/config/source/lastfm.ts create mode 100644 src/common/infrastructure/config/source/plex.ts create mode 100644 src/common/infrastructure/config/source/sources.ts create mode 100644 src/common/infrastructure/config/source/spotify.ts create mode 100644 src/common/infrastructure/config/source/subsonic.ts create mode 100644 src/common/infrastructure/config/source/tautulli.ts create mode 100644 src/common/infrastructure/typings/lastfm-node-client.d.ts create mode 100644 src/common/schema/aio.json diff --git a/.gitignore b/.gitignore index 757bc5f3..0c0c8cff 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ dist .idea/ !tsconfig.json +!./src/schema/*.json diff --git a/package-lock.json b/package-lock.json index cd2215df..50ce812a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,16 +37,19 @@ }, "devDependencies": { "@tsconfig/node18": "^1.0.1", + "@types/concat-stream": "^2.0.0", "@types/express": "^4.17.13", "@types/express-session": "^1.17.4", + "@types/formidable": "^2.0.5", "@types/node": "^18.0.0", "@types/passport": "^1.0.7", + "@types/superagent": "^4.1.16", "@types/triple-beam": "^1.3.2", "ts-essentials": "^9.1.2", "ts-migrate": "^0.1.35", "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", - "typescript": "^4.6.4", + "typescript": "^4.9.5", "typescript-json-schema": "~0.53" }, "engines": { @@ -146,18 +149,6 @@ } } }, - "node_modules/@babel/core/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/core/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1900,6 +1891,12 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, "node_modules/@babel/runtime": { "version": "7.20.13", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", @@ -2004,6 +2001,16 @@ "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", @@ -2175,13 +2182,13 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "node_modules/@nodelib/fs.scandir": { @@ -2306,6 +2313,15 @@ "@types/node": "*" } }, + "node_modules/@types/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -2320,6 +2336,12 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, + "node_modules/@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, "node_modules/@types/cors": { "version": "2.8.13", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", @@ -2360,6 +2382,15 @@ "@types/express": "*" } }, + "node_modules/@types/formidable": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.5.tgz", + "integrity": "sha512-uvMcdn/KK3maPOaVUAc3HEYbCEhjaGFwww4EsX6IJfWIJ1tzHtDHczuImH3GKdusPnAAmzB07St90uabZeCKPA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2414,6 +2445,16 @@ "@types/node": "*" } }, + "node_modules/@types/superagent": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", + "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", + "dev": true, + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, "node_modules/@types/triple-beam": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", @@ -2432,9 +2473,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3042,17 +3083,28 @@ } }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=12" + "node": ">=8" } }, "node_modules/clone-deep": { @@ -3507,9 +3559,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.289", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.289.tgz", - "integrity": "sha512-relLdMfPBxqGCxy7Gyfm1HcbRPcFUJdlgnCPVgQ23sr1TvUrRJz0/QPoGP0+x41wOVSTN/Wi3w6YDgHiHJGOzg==", + "version": "1.4.292", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.292.tgz", + "integrity": "sha512-ESWOSyJy5odDlE8wvh5NNAMORv4r6assPwIPGHEMWrWD0SONXcG/xT+9aD9CQyeRwyYDPo6dJT4Bbeg5uevVQQ==", "dev": true }, "node_modules/emoji-regex": { @@ -3817,18 +3869,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/espree/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/espree/node_modules/eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", @@ -5117,15 +5157,15 @@ "dev": true }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/json5-writer": { @@ -5220,97 +5260,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/log-update/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -6274,14 +6223,14 @@ } }, "node_modules/regexpu-core": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", - "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.0.tgz", + "integrity": "sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ==", "dev": true, "dependencies": { + "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.1.0", - "regjsgen": "^0.7.1", "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" @@ -6290,12 +6239,6 @@ "node": ">=4" } }, - "node_modules/regjsgen": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", - "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", - "dev": true - }, "node_modules/regjsparser": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", @@ -7510,183 +7453,83 @@ "typescript": ">4.0" } }, - "node_modules/ts-migrate/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/ts-migrate/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" }, - "engines": { - "node": ">=8" + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "node_modules/ts-migrate/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/ts-node/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, "bin": { - "json5": "lib/cli.js" + "acorn": "bin/acorn" }, "engines": { - "node": ">=6" + "node": ">=0.4.0" } }, - "node_modules/ts-migrate/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-migrate/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-migrate/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-migrate/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-migrate/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/ts-migrate/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-migrate/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" + "minimist": "^1.2.0" }, "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "json5": "lib/cli.js" } }, "node_modules/tslib": { @@ -7774,6 +7617,20 @@ "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", "dev": true }, + "node_modules/typescript-json-schema/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/typescript-json-schema/node_modules/safe-stable-stringify": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", @@ -7796,6 +7653,59 @@ "node": ">=4.2.0" } }, + "node_modules/typescript-json-schema/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/typescript-json-schema/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript-json-schema/node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/typescript-json-schema/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -8259,20 +8169,94 @@ } }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/wrappy": { @@ -8312,13 +8296,10 @@ } }, "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true }, "node_modules/yallist": { "version": "4.0.0", @@ -8326,30 +8307,84 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": ">=12" + "node": ">=8" } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, "engines": { - "node": ">=12" + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/yn": { diff --git a/package.json b/package.json index 3465e0e5..e78c1969 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,21 @@ "version": "0.4.0", "description": "scrobble plays from multiple sources to multiple clients", "main": "src/index.js", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "build": "tsc" + "build": "tsc", + "start": "node src/index.js", + "schema": "npm run -s schema-aio & npm run -s schema-sources & npm run -s schema-client", + "schema-client": "typescript-json-schema tsconfig.json ClientConfig --out src/common/schema/client.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", + "schema-sources": "typescript-json-schema tsconfig.json SourceConfig --out src/common/schema/source.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", + "schema-aio": "typescript-json-schema tsconfig.json AIOConfig --out src/common/schema/aio.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage" + }, + "exports": { + ".": { + "types": "./src/common/infrastructure/typings/lastfm-node-client.d.ts", + "import": "./src/index.js" + } }, "engines": { "node": ">=18.0.0", @@ -50,16 +62,19 @@ }, "devDependencies": { "@tsconfig/node18": "^1.0.1", + "@types/concat-stream": "^2.0.0", "@types/express": "^4.17.13", "@types/express-session": "^1.17.4", + "@types/formidable": "^2.0.5", "@types/node": "^18.0.0", "@types/passport": "^1.0.7", + "@types/superagent": "^4.1.16", "@types/triple-beam": "^1.3.2", "ts-essentials": "^9.1.2", "ts-migrate": "^0.1.35", "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", - "typescript": "^4.6.4", + "typescript": "^4.9.5", "typescript-json-schema": "~0.53" } } diff --git a/src/apis/AbstractApiClient.ts b/src/apis/AbstractApiClient.ts index e12cc270..48939f1e 100644 --- a/src/apis/AbstractApiClient.ts +++ b/src/apis/AbstractApiClient.ts @@ -1,17 +1,19 @@ -import {capitalize, createLabelledLogger} from "../utils.js"; +import {capitalize, createLabelledLogger} from "../utils"; +import {Logger} from "winston"; +import {PlayObject} from "../common/infrastructure/Atomic"; -export default class AbstractApiClient { - name; - type; - initialized = false; +export default abstract class AbstractApiClient { + name: string; + type: string; + initialized: boolean = false; - config; - options; - logger; + config: object; + options: object; + logger: Logger; client: any; - workingCredsPath: any; - redirectUri: any; + workingCredsPath?: string; + redirectUri?: string; constructor(type: any, name: any, config = {}, options = {}) { this.type = type; @@ -22,7 +24,7 @@ export default class AbstractApiClient { this.options = options; } - static formatPlayObj = (obj: any) => { + static formatPlayObj = (obj: any): PlayObject => { throw new Error('should be overridden'); } } diff --git a/src/apis/LastfmApiClient.ts b/src/apis/LastfmApiClient.ts index c1d33797..9b531915 100644 --- a/src/apis/LastfmApiClient.ts +++ b/src/apis/LastfmApiClient.ts @@ -1,8 +1,9 @@ -// @ts-expect-error TS(7016): Could not find a declaration file for module 'last... Remove this comment to see the full error message -import LastFm from "lastfm-node-client"; +import LastFm, {AuthGetSessionResponse, TrackObject, UserGetInfoResponse} from "lastfm-node-client"; import AbstractApiClient from "./AbstractApiClient.js"; import dayjs from "dayjs"; -import {readJson, sleep, writeFile} from "../utils.js"; +import {readJson, sleep, writeFile} from "../utils"; +import {PlayObject} from "../common/infrastructure/Atomic"; +import {LastfmData} from "../common/infrastructure/config/client/lastfm"; const badErrors = [ 'api key suspended', @@ -18,12 +19,11 @@ const retryErrors = [ 'rate limit' ] -// @ts-expect-error TS(2417): Class static side 'typeof LastfmApiClient' incorre... Remove this comment to see the full error message export default class LastfmApiClient extends AbstractApiClient { - user: any; + user?: string; - constructor(name: any, config = {}, options = {}) { + constructor(name: any, config: Partial, options = {}) { super('lastfm', name, config, options); // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{}'... Remove this comment to see the full error message const {redirectUri, apiKey, secret, session, configDir} = config; @@ -32,13 +32,12 @@ export default class LastfmApiClient extends AbstractApiClient { this.logger.warn("'apiKey' not found in config!"); } this.workingCredsPath = `${configDir}/currentCreds-lastfm-${name}.json`; - this.client = new LastFm(apiKey, secret, session); + this.client = new LastFm(apiKey as string, secret, session); } - static formatPlayObj = (obj: any) => { + static formatPlayObj = (obj: TrackObject): PlayObject => { const { artist: { - // last.fm doesn't seem consistent with which of these properties it returns... '#text': artists, name: artistName, }, @@ -48,7 +47,7 @@ export default class LastfmApiClient extends AbstractApiClient { }, duration, date: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message + // @ts-ignore uts: time, } = {}, '@attr': { @@ -61,7 +60,7 @@ export default class LastfmApiClient extends AbstractApiClient { let artistStrings = artists !== undefined ? artists.split(',') : [artistName]; return { data: { - artists: [...new Set(artistStrings)], + artists: [...new Set(artistStrings)] as string[], track: title, album, duration, @@ -78,8 +77,7 @@ export default class LastfmApiClient extends AbstractApiClient { } } - // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message - callApi = async (func: any, retries = 0) => { + callApi = async (func: any, retries = 0): Promise => { const { // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 2, @@ -88,7 +86,7 @@ export default class LastfmApiClient extends AbstractApiClient { } = this.config; try { - return await func(this.client); + return await func(this.client) as T; } catch (e) { const { // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message @@ -96,8 +94,8 @@ export default class LastfmApiClient extends AbstractApiClient { } = e; // for now check for exceptional errors by matching error code text const retryError = retryErrors.find(x => message.toLocaleLowerCase().includes(x)); - if(undefined !== retryError) { - if(retries < maxRequestRetries) { + if (undefined !== retryError) { + if (retries < maxRequestRetries) { const delay = (retries + 1) * retryMultiplier; this.logger.warn(`API call was not good but recoverable (${retryError}), retrying in ${delay} seconds...`); await sleep(delay * 1000); @@ -120,12 +118,10 @@ export default class LastfmApiClient extends AbstractApiClient { } authenticate = async (token: any) => { - const sessionRes = await this.client.authGetSession({token}); + const sessionRes: AuthGetSessionResponse = await this.client.authGetSession({token}); const { session: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message key: sessionKey, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name, // username } = {} } = sessionRes; @@ -157,10 +153,9 @@ export default class LastfmApiClient extends AbstractApiClient { return false; } try { - const infoResp = await this.callApi((client: any) => client.userGetInfo()); + const infoResp = await this.callApi((client: any) => client.userGetInfo()); const { user: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name, } = {} } = infoResp; diff --git a/src/clients/AbstractScrobbleClient.ts b/src/clients/AbstractScrobbleClient.ts index 2b986b29..f2ddad3d 100644 --- a/src/clients/AbstractScrobbleClient.ts +++ b/src/clients/AbstractScrobbleClient.ts @@ -1,33 +1,36 @@ -import dayjs from "dayjs"; -import {buildTrackString, capitalize, createLabelledLogger, playObjDataMatch} from "../utils.js"; -import {INITIALIZED, INITIALIZING, NOT_INITIALIZED} from "../common/index.js"; +import dayjs, {Dayjs} from "dayjs"; +import {buildTrackString, capitalize, createLabelledLogger, playObjDataMatch} from "../utils"; +import {ClientType, INITIALIZED, INITIALIZING, InitState, NOT_INITIALIZED} from "../common/infrastructure/Atomic"; +import {Logger} from "winston"; +import {CommonClientConfig} from "../common/infrastructure/config/client"; +import {ClientConfig} from "../common/infrastructure/config/client/clients"; -export default class AbstractScrobbleClient { +export default abstract class AbstractScrobbleClient { - name; - type; + name: string; + type: ClientType; - #initState = NOT_INITIALIZED; + #initState: InitState = NOT_INITIALIZED; - requiresAuth = false; - requiresAuthInteraction = false; - authed = false; + requiresAuth: boolean = false; + requiresAuthInteraction: boolean = false; + authed: boolean = false; recentScrobbles = []; scrobbledPlayObjs = []; - newestScrobbleTime: any; - oldestScrobbleTime = dayjs(); - tracksScrobbled = 0; + newestScrobbleTime?: Dayjs + oldestScrobbleTime: Dayjs = dayjs(); + tracksScrobbled: number = 0; - lastScrobbleCheck = dayjs(); - refreshEnabled; - checkExistingScrobbles; + lastScrobbleCheck: Dayjs = dayjs(); + refreshEnabled: boolean; + checkExistingScrobbles: boolean; verboseOptions; - config; - logger; + config: CommonClientConfig; + logger: Logger; - constructor(type: any, name: any, config = {}) { + constructor(type: any, name: any, config: CommonClientConfig) { this.type = type; this.name = name; const identifier = `Client ${capitalize(this.type)} - ${name}`; diff --git a/src/clients/LastfmScrobbler.ts b/src/clients/LastfmScrobbler.ts index 46922f4f..22761c2b 100644 --- a/src/clients/LastfmScrobbler.ts +++ b/src/clients/LastfmScrobbler.ts @@ -1,4 +1,4 @@ -import AbstractScrobbleClient from "./AbstractScrobbleClient.js"; +import AbstractScrobbleClient from "./AbstractScrobbleClient"; import dayjs from 'dayjs'; import { @@ -7,9 +7,11 @@ import { setIntersection, sleep, sortByPlayDate, truncateStringToLength, -} from "../utils.js"; -import LastfmApiClient from "../apis/LastfmApiClient.js"; -import {INITIALIZING} from "../common/index.js"; +} from "../utils"; +import LastfmApiClient from "../apis/LastfmApiClient"; +import {INITIALIZING} from "../common/infrastructure/Atomic"; +import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm"; +import {TrackScrobbleResponse, UserGetRecentTracksResponse} from "lastfm-node-client"; export default class LastfmScrobbler extends AbstractScrobbleClient { @@ -17,10 +19,12 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { requiresAuth = true; requiresAuthInteraction = true; - constructor(name: any, config = {}, options = {}) { + declare config: LastfmClientConfig; + + constructor(name: any, config: LastfmClientConfig, options = {}) { // @ts-expect-error TS(2554): Expected 2-3 arguments, but got 4. super('lastfm', name, config, options); - this.api = new LastfmApiClient(name, config, options) + this.api = new LastfmApiClient(name, config.data, options) } formatPlayObj = (obj: any) => LastfmApiClient.formatPlayObj(obj); @@ -46,7 +50,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { refreshScrobbles = async () => { if (this.refreshEnabled) { this.logger.debug('Refreshing recent scrobbles'); - const resp = await this.api.callApi((client: any) => client.userGetRecentTracks({user: this.api.user, limit: 20, extended: true})); + const resp = await this.api.callApi((client: any) => client.userGetRecentTracks({user: this.api.user, limit: 20, extended: true})); const { recenttracks: { track: list = [], @@ -294,22 +298,20 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { const scrobblePayload = removeUndefinedKeys(rawPayload); try { - const response = await this.api.callApi((client: any) => client.trackScrobble( + const response = await this.api.callApi((client: any) => client.trackScrobble( scrobblePayload)); const { scrobbles: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message '@attr': { accepted = 0, ignored = 0, - code, - }, + code = undefined, + } = {}, scrobble: { track: { // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message '#text': trackName, } = {}, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message timestamp, ignoredMessage: { // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message diff --git a/src/clients/MalojaScrobbler.ts b/src/clients/MalojaScrobbler.ts index 114d0e0c..72dc22ad 100644 --- a/src/clients/MalojaScrobbler.ts +++ b/src/clients/MalojaScrobbler.ts @@ -1,5 +1,4 @@ -import AbstractScrobbleClient from "./AbstractScrobbleClient.js"; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'supe... Remove this comment to see the full error message +import AbstractScrobbleClient from "./AbstractScrobbleClient"; import request from 'superagent'; import dayjs from 'dayjs'; import compareVersions from 'compare-versions'; @@ -11,8 +10,9 @@ import { sortByPlayDate, truncateStringToLength, parseRetryAfterSecsFromObj -} from "../utils.js"; -import {INITIALIZING} from "../common/index.js"; +} from "../utils"; +import {INITIALIZING} from "../common/infrastructure/Atomic"; +import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja"; const feat = ["ft.", "ft", "feat.", "feat", "featuring", "Ft.", "Ft", "Feat.", "Feat", "Featuring"]; @@ -22,7 +22,9 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { serverIsHealthy = false; serverVersion: any; - constructor(name: any, config = {}, options = {}) { + declare config: MalojaClientConfig + + constructor(name: any, config: MalojaClientConfig, options = {}) { // @ts-expect-error TS(2554): Expected 2-3 arguments, but got 4. super('maloja', name, config, options); // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. diff --git a/src/clients/ScrobbleClients.ts b/src/clients/ScrobbleClients.ts index 22d1d698..34bae35b 100644 --- a/src/clients/ScrobbleClients.ts +++ b/src/clients/ScrobbleClients.ts @@ -1,40 +1,48 @@ import dayjs from "dayjs"; import { + createAjvFactory, createLabelledLogger, isValidConfigStructure, playObjDataMatch, readJson, - returnDuplicateStrings -} from "../utils.js"; -import MalojaScrobbler from "./MalojaScrobbler.js"; -import LastfmScrobbler from "./LastfmScrobbler.js"; + returnDuplicateStrings, validateJson +} from "../utils"; +import MalojaScrobbler from "./MalojaScrobbler"; +import LastfmScrobbler from "./LastfmScrobbler"; +import {clientTypes, ConfigMeta} from "../common/infrastructure/Atomic"; +import {AIOConfig} from "../common/infrastructure/config/aioConfig"; +import * as aioSchema from '../common/schema/aio.json'; +import * as clientSchema from '../common/schema/client.json'; +import {ClientAIOConfig, ClientConfig} from "../common/infrastructure/config/client/clients"; +import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja"; +import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm"; + +type groupedNamedConfigs = {[key: string]: ParsedConfig[]}; + +type ParsedConfig = ClientAIOConfig & ConfigMeta; export default class ScrobbleClients { /** @type AbstractScrobbleClient[] */ - clients = []; + clients: (MalojaScrobbler | LastfmScrobbler)[] = []; logger; configDir; - clientTypes = ['maloja','lastfm']; - constructor(configDir: any) { this.configDir = configDir; this.logger = createLabelledLogger('scrobblers', 'Scrobblers'); } getByName = (name: any) => { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. return this.clients.find(x => x.name === name); } getByType = (type: any) => { - // @ts-expect-error TS(2339): Property 'type' does not exist on type 'never'. return this.clients.filter(x => x.type === type); } buildClientsFromConfig = async () => { - let configs = []; + let configs: ParsedConfig[] = []; let configFile; try { @@ -45,23 +53,24 @@ export default class ScrobbleClients { } let clientDefaults = {}; if (configFile !== undefined) { + const aioConfig = validateJson(configFile, aioSchema, this.logger); const { clients: mainConfigClientConfigs = [], clientDefaults: cd = {}, - } = configFile; + } = aioConfig; clientDefaults = cd; - const validMainConfigs = mainConfigClientConfigs.reduce((acc: any, curr: any, i: any) => { - if(curr === null) { - this.logger.error(`The client config entry at index ${i} in config.json is null but should be an object, will not parse`); - return acc; - } - if(typeof curr !== 'object') { - this.logger.error(`The client config entry at index ${i} in config.json should be an object, will not parse`); - return acc; - } - return acc.concat(curr); - }, []); - for (const c of validMainConfigs) { + // const validMainConfigs = mainConfigClientConfigs.reduce((acc: any, curr: any, i: any) => { + // if(curr === null) { + // this.logger.error(`The client config entry at index ${i} in config.json is null but should be an object, will not parse`); + // return acc; + // } + // if(typeof curr !== 'object') { + // this.logger.error(`The client config entry at index ${i} in config.json should be an object, will not parse`); + // return acc; + // } + // return acc.concat(curr); + // }, []); + for (const c of mainConfigClientConfigs) { const {name = 'unnamed'} = c; configs.push({...c, name, @@ -71,7 +80,7 @@ export default class ScrobbleClients { } } - for (const clientType of this.clientTypes) { + for (const clientType of clientTypes) { let defaultConfigureAs = 'client'; switch (clientType) { case 'maloja': @@ -86,6 +95,7 @@ export default class ScrobbleClients { mode: 'single', data: { url, + // @ts-ignore apiKey } }) @@ -104,6 +114,7 @@ export default class ScrobbleClients { name: 'unnamed', source: 'ENV', mode: 'single', + // @ts-ignore data: lfm }) } @@ -119,25 +130,34 @@ export default class ScrobbleClients { continue; } if (rawClientConfigs !== undefined) { - let clientConfigs = []; + let clientConfigs: ParsedConfig[] = []; if (Array.isArray(rawClientConfigs)) { clientConfigs = rawClientConfigs; } else if(rawClientConfigs === null) { this.logger.error(`${clientType}.json contained no data`); continue; - } else if (typeof rawClientConfigs === 'object') { - // backwards compatibility, assuming its single-user mode - this.logger.warn(`DEPRECATED: Starting in 0.4 configurations in all [type].json files (${clientType}.json) must be in an array.`); - if (rawClientConfigs.data === undefined) { - clientConfigs = [{data: rawClientConfigs, mode: 'single', name: 'unnamed'}]; - } else { - clientConfigs = [rawClientConfigs]; - } } else { this.logger.error(`All top level data from ${clientType}.json must be an array of objects, will not parse configs from file`); continue; } - for (const [i,m] of clientConfigs.entries()) { + for(const [i,rawConf] of rawClientConfigs.entries()) { + try { + const validConfig = validateJson(rawConf, clientSchema, this.logger); + // @ts-ignore + const {configureAs = defaultConfigureAs} = validConfig; + if (configureAs === 'client') { + const parsedConfig: ParsedConfig = { + ...rawConf, + source: `${clientType}.json`, + type: clientType + } + configs.push(parsedConfig); + } + } catch (e: any) { + this.logger.error(`The config entry at index ${i} from ${clientType}.json was not valid`); + } + } +/* for (const [i,m] of clientConfigs.entries()) { if(m === null) { this.logger.error(`The config entry at index ${i} from ${clientType}.json is null`); continue; @@ -152,32 +172,30 @@ export default class ScrobbleClients { m.type = clientType; configs.push(m); } - } + }*/ } } // we have all possible client configurations so we'll check they are minimally valid - const validConfigs = configs.reduce((acc, c) => { + /*const validConfigs = configs.reduce((acc, c) => { const isValid = isValidConfigStructure(c, {type: true, data: true}); if (isValid !== true) { this.logger.error(`Client config from ${c.source} with name [${c.name || 'unnamed'}] of type [${c.type || 'unknown'}] will not be used because it has structural errors: ${isValid.join(' | ')}`); return acc; } return acc.concat(c); - }, []); + }, []);*/ // all client configs are minimally valid // now check that names are unique - const nameGroupedConfigs = validConfigs.reduce((acc: any, curr: any) => { + const nameGroupedConfigs = configs.reduce((acc: groupedNamedConfigs, curr: ParsedConfig) => { const {name = 'unnamed'} = curr; const {[name]: n = []} = acc; return {...acc, [name]: [...n, curr]}; }, {}); - let noConflictConfigs: any = []; + let noConflictConfigs: ParsedConfig[] = []; for (const [name, configs] of Object.entries(nameGroupedConfigs)) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. if (configs.length > 1) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. const sources = configs.map((c: any) => `Config object from ${c.source} of type [${c.type}]`); this.logger.error(`The following clients will not be built because of config naming conflicts (they have the same name of "${name}"): ${sources.join('\n')}`); @@ -185,14 +203,13 @@ ${sources.join('\n')}`); this.logger.info('HINT: "unnamed" configs occur when using ENVs, if a multi-user mode config does not have a "name" property, or if a config is built in single-user mode'); } } else { - // @ts-expect-error TS(2488): Type 'unknown' must have a '[Symbol.iterator]()' m... Remove this comment to see the full error message noConflictConfigs = [...noConflictConfigs, ...configs]; } } // finally! all configs are valid, structurally, and can now be passed to addClient // just need to re-map unnnamed to default - const finalConfigs = noConflictConfigs.map(({name = 'unnamed', ...x}) => ({ + const finalConfigs: ParsedConfig[] = noConflictConfigs.map(({name = 'unnamed', ...x}) => ({ ...x, name })); @@ -206,11 +223,11 @@ ${sources.join('\n')}`); } } - addClient = async (clientConfig: any, defaults = {}) => { - const isValidConfig = isValidConfigStructure(clientConfig, {name: true, data: true, type: true}); + addClient = async (clientConfig: ParsedConfig, defaults = {}) => { +/* const isValidConfig = isValidConfigStructure(clientConfig, {name: true, data: true, type: true}); if (isValidConfig !== true) { throw new Error(`Config object from ${clientConfig.source || 'unknown'} with name [${clientConfig.name || 'unnamed'}] of type [${clientConfig.type || 'unknown'}] has errors: ${isValidConfig.join(' | ')}`) - } + }*/ const {type, name, data: d = {}} = clientConfig; // add defaults const data = {...defaults, ...d}; @@ -218,10 +235,10 @@ ${sources.join('\n')}`); this.logger.debug(`(${name}) Constructing ${type} client...`); switch (type) { case 'maloja': - newClient = new MalojaScrobbler(name, data); + newClient = new MalojaScrobbler(name, ({...clientConfig, data} as unknown as MalojaClientConfig)); break; case 'lastfm': - newClient = new LastfmScrobbler(name, {...data, configDir: this.configDir}); + newClient = new LastfmScrobbler(name, {...clientConfig, data: {configDir: this.configDir, data} } as unknown as LastfmClientConfig ); break; default: break; @@ -253,7 +270,6 @@ ${sources.join('\n')}`); this.logger.info(`(${name}) ${type} client auth OK`); } } - // @ts-expect-error TS(2345): Argument of type 'MalojaScrobbler | LastfmScrobble... Remove this comment to see the full error message this.clients.push(newClient); } @@ -282,57 +298,40 @@ ${sources.join('\n')}`); } for (const client of this.clients) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. if (scrobbleTo.length > 0 && !scrobbleTo.includes(client.name)) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.debug(`Client '${client.name}' was filtered out by '${scrobbleFrom}'`); continue; } - // @ts-expect-error TS(2339): Property 'initialized' does not exist on type 'nev... Remove this comment to see the full error message if(!client.initialized) { - // @ts-expect-error TS(2339): Property 'initializing' does not exist on type 'ne... Remove this comment to see the full error message if(client.initializing) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because it is still initializing`); continue; } - // @ts-expect-error TS(2339): Property 'initialize' does not exist on type 'neve... Remove this comment to see the full error message if(!(await client.initialize())) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because it could not be initialized`); continue; } } - // @ts-expect-error TS(2339): Property 'requiresAuth' does not exist on type 'ne... Remove this comment to see the full error message if(client.requiresAuth && !client.authed) { - // @ts-expect-error TS(2339): Property 'requiresAuthInteraction' does not exist ... Remove this comment to see the full error message if (client.requiresAuthInteraction) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because user interaction is required for authentication`); continue; - // @ts-expect-error TS(2339): Property 'testAuth' does not exist on type 'never'... Remove this comment to see the full error message } else if (!(await client.testAuth())) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because auth test failed`); continue; } } - // @ts-expect-error TS(2339): Property 'isReady' does not exist on type 'never'. if(!(await client.isReady())) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.warn(`Cannot scrobble to Client '${client.name}' because it is not ready`); continue; } - // @ts-expect-error TS(2339): Property 'scrobblesLastCheckedAt' does not exist o... Remove this comment to see the full error message if (forceRefresh || client.scrobblesLastCheckedAt().unix() < checkTime.unix()) { try { - // @ts-expect-error TS(2339): Property 'refreshScrobbles' does not exist on type... Remove this comment to see the full error message await client.refreshScrobbles(); } catch(e) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.error(`Encountered error while refreshing scrobbles for ${client.name}`); this.logger.error(e); } @@ -344,11 +343,8 @@ ${sources.join('\n')}`); newFromSource = false, } = {} } = playObj; - // @ts-expect-error TS(2339): Property 'timeFrameIsValid' does not exist on type... Remove this comment to see the full error message if (client.timeFrameIsValid(playObj, newFromSource) && !(await client.alreadyScrobbled(playObj, newFromSource))) { - // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never'... Remove this comment to see the full error message await client.scrobble(playObj) - // @ts-expect-error TS(2339): Property 'tracksScrobbled' does not exist on type ... Remove this comment to see the full error message client.tracksScrobbled++; // since this is what we return to the source only add to tracksScrobbled if not already in array // (source should only know that a track was scrobbled (binary) -- doesn't care if it was scrobbled more than once @@ -358,7 +354,6 @@ ${sources.join('\n')}`); } } } catch(e) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. this.logger.error(`Encountered error while in scrobble loop for ${client.name}`); this.logger.error(e); // for now just stop scrobbling plays for this client and move on. the client should deal with logging the issue diff --git a/src/common/index.ts b/src/common/index.ts index 01f1f5d0..b302a63e 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,19 +1,7 @@ import path from 'path'; -import { fileURLToPath } from 'url'; -export const NOT_INITIALIZED = 0; -export const INITIALIZING = 1; -export const INITIALIZED = 2; -export const initStates = [NOT_INITIALIZED, INITIALIZING, INITIALIZED]; - -export const NOT_READY = 0; -export const GETTING_READY = 1; -export const READY = 2; - -export const readyStates = [NOT_READY, GETTING_READY, READY]; - -// @ts-expect-error TS(1343): The 'import.meta' meta-property is only allowed wh... Remove this comment to see the full error message -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +// const __filename = fileURLToPath(import.meta.url); +// const __dirname = path.dirname(__filename); export const projectDir = path.resolve(__dirname, '../../'); +export const configDir: string = path.resolve(projectDir, './config'); diff --git a/src/common/infrastructure/Atomic.ts b/src/common/infrastructure/Atomic.ts new file mode 100644 index 00000000..9e514ee6 --- /dev/null +++ b/src/common/infrastructure/Atomic.ts @@ -0,0 +1,60 @@ +import {Dayjs} from "dayjs"; + +export type SourceType = 'spotify' | 'plex' | 'tautulli' | 'subsonic' | 'jellyfin' | 'lastfm' | 'deezer'; +export const sourceTypes: SourceType[] = ['spotify', 'plex', 'tautulli', 'subsonic', 'jellyfin', 'lastfm', 'deezer']; + +export type ClientType = 'maloja' | 'lastfm'; +export const clientTypes: ClientType[] = ['maloja', 'lastfm']; + +export type InitState = 0 | 1 | 2; +export const NOT_INITIALIZED: InitState = 0; +export const INITIALIZING: InitState = 1; +export const INITIALIZED: InitState = 2; +export const initStates: InitState[] = [NOT_INITIALIZED, INITIALIZING, INITIALIZED]; + +export type ReadyState = 0 | 1 | 2; +export const NOT_READY: ReadyState = 0; +export const GETTING_READY: ReadyState = 1; +export const READY: ReadyState = 2; +export const readyStates: ReadyState[] = [NOT_READY, GETTING_READY, READY]; + +export interface InternalConfig { + localUrl: string + configDir: string +} + +export interface ConfigMeta { + source: string + mode?: string + configureAs: string +} + +export interface PlayData { + artists?: string[] + album?: string + track?: string + duration?: number + playDate?: Dayjs +} + +export interface PlayMeta { + source?: string + sourceId?: string + newFromSource?: boolean + url?: { + web: string + [key: string]: string + } + trackLength?: number + user?: string + mediaType?: string + server?: string + library?: string + + [key: string]: any +} + +export interface PlayObject { + data: PlayData, + meta: PlayMeta +} diff --git a/src/common/infrastructure/config/aioConfig.ts b/src/common/infrastructure/config/aioConfig.ts new file mode 100644 index 00000000..d1c5ffca --- /dev/null +++ b/src/common/infrastructure/config/aioConfig.ts @@ -0,0 +1,11 @@ +import {SourceRetryOptions} from "./source"; +import {RequestRetryOptions} from "./common"; +import {SourceAIOConfig} from "./source/sources"; +import {ClientAIOConfig} from "./client/clients"; + +export interface AIOConfig { + sourceDefaults?: SourceRetryOptions + clientDefaults?: RequestRetryOptions + sources?: SourceAIOConfig[] + clients?: ClientAIOConfig[] +} diff --git a/src/common/infrastructure/config/client/clients.ts b/src/common/infrastructure/config/client/clients.ts new file mode 100644 index 00000000..d4151a46 --- /dev/null +++ b/src/common/infrastructure/config/client/clients.ts @@ -0,0 +1,6 @@ +import {MalojaClientAIOConfig, MalojaClientConfig} from "./maloja"; +import {LastfmClientAIOConfig, LastfmClientConfig} from "./lastfm"; + +export type ClientConfig = MalojaClientConfig | LastfmClientConfig; + +export type ClientAIOConfig = MalojaClientAIOConfig | LastfmClientAIOConfig; diff --git a/src/common/infrastructure/config/client/index.ts b/src/common/infrastructure/config/client/index.ts new file mode 100644 index 00000000..8607fc5b --- /dev/null +++ b/src/common/infrastructure/config/client/index.ts @@ -0,0 +1,38 @@ +import {CommonConfig, CommonData} from "../common"; + +export interface MatchLoggingOptions { + onNoMatch?: boolean + onMatch?: boolean + confidenceBreakdown?: boolean +} + +export interface CommonClientData extends CommonData { + /** + * default # of http request retries a client can make before error is thrown. + * + * @default 1 + * */ + maxRequestRetries?: number + /** + * default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying). + * + * @default 1.5 + * */ + retryMultiplier?: number + + options?: { + refreshEnabled?: boolean + checkExistingScrobbles?: boolean + verbose?: { + match?: MatchLoggingOptions + } + } +} + +export interface CommonClientConfig extends CommonConfig { + /** + * Unique identifier for this client. Used with sources to restrict where scrobbles are sent. + * */ + name: string + data?: CommonClientData +} diff --git a/src/common/infrastructure/config/client/lastfm.ts b/src/common/infrastructure/config/client/lastfm.ts new file mode 100644 index 00000000..79cc5a22 --- /dev/null +++ b/src/common/infrastructure/config/client/lastfm.ts @@ -0,0 +1,31 @@ +import {CommonClientConfig, CommonClientData} from "./index"; + +export interface LastfmData { + /** + * API Key generated from Last.fm account + * */ + apiKey: string + /** + * Secret generated from Last.fm account + * */ + secret: string + /** + * Optional session id returned from a completed auth flow + * */ + session?: string + /** + * Optional URI to use for callback. Specify this if callback should be different than the default. MUST have "lastfm/callback" in the URL somewhere. + * + * @default "http://localhost:9078/lastfm/callback" + * */ + redirectUri?: string +} + +export interface LastfmClientConfig extends CommonClientConfig { + configureAs?: 'client' | 'source' + data: CommonClientData & LastfmData +} + +export interface LastfmClientAIOConfig extends LastfmClientConfig { + type: 'lastfm' +} diff --git a/src/common/infrastructure/config/client/maloja.ts b/src/common/infrastructure/config/client/maloja.ts new file mode 100644 index 00000000..35afdc97 --- /dev/null +++ b/src/common/infrastructure/config/client/maloja.ts @@ -0,0 +1,19 @@ +import {CommonClientConfig, CommonClientData} from "./index"; + +export interface MalojaClientConfig extends CommonClientConfig { + data: CommonClientData & { + /** + * URL for maloja server + * */ + url: string + /** + * API Key for Maloja server + * */ + apiKey: string + + } +} + +export interface MalojaClientAIOConfig extends MalojaClientConfig { + type: 'maloja' +} diff --git a/src/common/infrastructure/config/common.ts b/src/common/infrastructure/config/common.ts new file mode 100644 index 00000000..b8950014 --- /dev/null +++ b/src/common/infrastructure/config/common.ts @@ -0,0 +1,24 @@ +export interface CommonConfig { + name?: string + data?: CommonData +} + +export interface CommonData { + [key: string]: any + options?: Record +} + +export interface RequestRetryOptions { + /** + * default # of http request retries a source can make before error is thrown + * + * @default 1 + * */ + maxRequestRetries?: number + /** + * default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying) + * + * @default 1.5 + * */ + retryMultiplier?: number +} diff --git a/src/common/infrastructure/config/source/deezer.ts b/src/common/infrastructure/config/source/deezer.ts new file mode 100644 index 00000000..017db12a --- /dev/null +++ b/src/common/infrastructure/config/source/deezer.ts @@ -0,0 +1,31 @@ +import {CommonSourceConfig, CommonSourceData} from "./index"; + +export interface DeezerData extends CommonSourceData { + /** + * deezer client id + * */ + clientId: string + /** + * deezer client secret + * */ + clientSecret: string + /** + * deezer redirect URI -- required only if not the default shown here. URI must end in "callback" + * + * @default "http://localhost:9078/deezer/callback" + * */ + redirectUri: string + /** + * optional, how long to wait before calling spotify for new tracks (in seconds) + * + * @default 60 + * */ + interval?: number +} +export interface DeezerSourceConfig extends CommonSourceConfig { + data: DeezerData +} + +export interface DeezerSourceAIOConfig extends DeezerSourceConfig { + type: 'deezer' +} diff --git a/src/common/infrastructure/config/source/index.ts b/src/common/infrastructure/config/source/index.ts new file mode 100644 index 00000000..cd0f0c4d --- /dev/null +++ b/src/common/infrastructure/config/source/index.ts @@ -0,0 +1,26 @@ +import {CommonConfig, CommonData, RequestRetryOptions} from "../common"; + +export interface SourceRetryOptions extends RequestRetryOptions { + /** + * default # of automatic polling restarts on error + * + * @default 0 + * */ + maxPollRetries?: number +} + +export interface CommonSourceData extends CommonData, SourceRetryOptions { +} + +export interface CommonSourceConfig extends CommonConfig { + /** + * Unique identifier for this source. + * */ + name?: string + /** + * Restrict scrobbling tracks played from this source to Clients with names from this list + * */ + clients?: string[] + data?: CommonSourceData +} + diff --git a/src/common/infrastructure/config/source/jellyfin.ts b/src/common/infrastructure/config/source/jellyfin.ts new file mode 100644 index 00000000..4dec0df3 --- /dev/null +++ b/src/common/infrastructure/config/source/jellyfin.ts @@ -0,0 +1,22 @@ +import {CommonSourceConfig, CommonSourceData} from "./index"; + +export interface JellySourceConfig extends CommonSourceConfig { + data: CommonSourceData & { + /** + * optional list of users to scrobble tracks from + * + * If none are provided tracks from all users will be scrobbled + * */ + user?: string + /** + * optional list of servers to scrobble tracks from + * + * If none are provided tracks from all servers will be scrobbled + * */ + servers?: string + } +} + +export interface JellySourceAIOConfig extends JellySourceConfig { + type: 'jellyfin' +} diff --git a/src/common/infrastructure/config/source/lastfm.ts b/src/common/infrastructure/config/source/lastfm.ts new file mode 100644 index 00000000..07e763e4 --- /dev/null +++ b/src/common/infrastructure/config/source/lastfm.ts @@ -0,0 +1,11 @@ +import {LastfmData} from "../client/lastfm"; +import {CommonSourceConfig, CommonSourceData} from "./index"; + +export interface LastfmSourceConfig extends CommonSourceConfig { + configureAs?: 'source' + data: CommonSourceData & LastfmData +} + +export interface LastFmSouceAIOConfig extends LastfmSourceConfig { + type: 'lastfm' +} diff --git a/src/common/infrastructure/config/source/plex.ts b/src/common/infrastructure/config/source/plex.ts new file mode 100644 index 00000000..f961b7fa --- /dev/null +++ b/src/common/infrastructure/config/source/plex.ts @@ -0,0 +1,28 @@ +import {CommonSourceConfig, CommonSourceData} from "./index"; + +export interface PlexSourceConfig extends CommonSourceConfig { + data: CommonSourceData & { + /** + * optional list of users to scrobble tracks from + * + * If none are provided tracks from all users will be scrobbled + * */ + user?: string + /** + * optional list of libraries to scrobble tracks from + * + * If none are provided tracks from all libraries will be scrobbled + * */ + libraries?: string + /** + * optional list of servers to scrobble tracks from + * + * If none are provided tracks from all servers will be scrobbled + * */ + servers?: string + } +} + +export interface PlexSourceAIOConfig extends PlexSourceConfig { + type: 'plex' +} diff --git a/src/common/infrastructure/config/source/sources.ts b/src/common/infrastructure/config/source/sources.ts new file mode 100644 index 00000000..292bfd13 --- /dev/null +++ b/src/common/infrastructure/config/source/sources.ts @@ -0,0 +1,11 @@ +import {SpotifySourceAIOConfig, SpotifySourceConfig} from "./spotify"; +import {PlexSourceAIOConfig, PlexSourceConfig} from "./plex"; +import {TautulliSourceAIOConfig, TautulliSourceConfig} from "./tautulli"; +import {DeezerSourceAIOConfig, DeezerSourceConfig} from "./deezer"; +import {SubsonicSourceAIOConfig, SubSonicSourceConfig} from "./subsonic"; +import {JellySourceAIOConfig, JellySourceConfig} from "./jellyfin"; +import {LastFmSouceAIOConfig, LastfmSourceConfig} from "./lastfm"; + +export type SourceConfig = SpotifySourceConfig | PlexSourceConfig | TautulliSourceConfig | DeezerSourceConfig | SubSonicSourceConfig | JellySourceConfig | LastfmSourceConfig; + +export type SourceAIOConfig = SpotifySourceAIOConfig | PlexSourceAIOConfig | TautulliSourceAIOConfig | DeezerSourceAIOConfig | SubsonicSourceAIOConfig | JellySourceAIOConfig | LastFmSouceAIOConfig; diff --git a/src/common/infrastructure/config/source/spotify.ts b/src/common/infrastructure/config/source/spotify.ts new file mode 100644 index 00000000..acbfadcc --- /dev/null +++ b/src/common/infrastructure/config/source/spotify.ts @@ -0,0 +1,32 @@ +import {CommonSourceConfig, CommonSourceData} from "./index"; + +export interface SpotifySourceData extends CommonSourceData { + /** + * spotify client id + * */ + clientId: string + /** + * spotify client secret + * */ + clientSecret: string + /** + * spotify redirect URI -- required only if not the default shown here. URI must end in "callback" + * + * @default "http://localhost:9078/callback" + * */ + redirectUri: string + /** + * optional, how long to wait before calling spotify for new tracks (in seconds) + * + * @default 60 + * */ + interval?: number +} + +export interface SpotifySourceConfig extends CommonSourceConfig { + data: SpotifySourceData +} + +export interface SpotifySourceAIOConfig extends SpotifySourceConfig { + type: 'spotify' +} diff --git a/src/common/infrastructure/config/source/subsonic.ts b/src/common/infrastructure/config/source/subsonic.ts new file mode 100644 index 00000000..a8af2e59 --- /dev/null +++ b/src/common/infrastructure/config/source/subsonic.ts @@ -0,0 +1,17 @@ +import {CommonSourceConfig, CommonSourceData} from "./index"; + +export interface SubsonicData extends CommonSourceData { + /** + * URL of the subsonic media server to query + * */ + url: string + user: string + password: string +} +export interface SubSonicSourceConfig extends CommonSourceConfig { + data: SubsonicData +} + +export interface SubsonicSourceAIOConfig extends SubSonicSourceConfig { + type: 'subsonic' +} diff --git a/src/common/infrastructure/config/source/tautulli.ts b/src/common/infrastructure/config/source/tautulli.ts new file mode 100644 index 00000000..32127a55 --- /dev/null +++ b/src/common/infrastructure/config/source/tautulli.ts @@ -0,0 +1,8 @@ +import {PlexSourceConfig} from "./plex"; + +export interface TautulliSourceConfig extends PlexSourceConfig { +} + +export interface TautulliSourceAIOConfig extends TautulliSourceConfig { + type: 'tautulli' +} diff --git a/src/common/infrastructure/typings/lastfm-node-client.d.ts b/src/common/infrastructure/typings/lastfm-node-client.d.ts new file mode 100644 index 00000000..d31421f6 --- /dev/null +++ b/src/common/infrastructure/typings/lastfm-node-client.d.ts @@ -0,0 +1,108 @@ +declare module 'lastfm-node-client' { + /** + * Must REMOVE (unset) any undefined/null properties from payload before sending or LastFM will return an error + * */ + export interface TrackScrobblePayload { + /** + * join multiple artists with ', ' + * */ + artist: string + + /** + * track title + * */ + track: string + + /** + * Unix timestamp of time track should be scrobbled at + * */ + timestamp: number + /** + * length of track in seconds + * */ + duration?: number + + album?: string + + albumArtist?: string + } + + export interface TrackScrobbleResponse { + scrobbles: { + '@attr': { + accepted: number, + ignored: number + code: number + }, + scrobble?: { + track: { + '#text': string + }, + timestamp: number, + ignoredMessage: { + code: number + '#text': string + } + } + } + } + + export interface AuthGetSessionPayload { + token: string + } + + export interface AuthGetSessionResponse { + session: { + key: string + name: string + } + } + + export interface UserGetInfoResponse { + user: { + name: string + } + } + + export interface UserGetRecentTracksPayload { + user: string + limit?: number + extended?: boolean + } + + export interface UserGetRecentTracksResponse { + recenttracks: { + track: TrackObject[] + } + } + + export interface TrackObject { + artist: { + '#text': string, + name: string, + }, + name: string, + album: { + '#text': string, + }, + duration: number, + date?: { + // @ts-ignore + uts: number, + }, + '@attr'?: { + nowplaying: 'true' | 'false' + } + url: string, + mbid: string, + } + + export default class LastFM { + constructor(apiKey: string, secret?: string, session?: string); + + trackScrobble(params: TrackScrobblePayload): Promise + authGetSession(params: AuthGetSessionPayload): Promise + userGetInfo(): Promise + userGetRecentTracks(params: UserGetRecentTracksPayload): Promise + } +} diff --git a/src/common/schema/aio.json b/src/common/schema/aio.json new file mode 100644 index 00000000..d08608c5 --- /dev/null +++ b/src/common/schema/aio.json @@ -0,0 +1,677 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "CommonClientData": { + "properties": { + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a client can make before error is thrown.", + "type": "number" + }, + "options": { + "properties": { + "checkExistingScrobbles": { + "type": "boolean" + }, + "refreshEnabled": { + "type": "boolean" + }, + "verbose": { + "properties": { + "match": { + "$ref": "#/definitions/MatchLoggingOptions" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying).", + "type": "number" + } + }, + "type": "object" + }, + "CommonSourceData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "DeezerData": { + "properties": { + "clientId": { + "description": "deezer client id", + "type": "string" + }, + "clientSecret": { + "description": "deezer client secret", + "type": "string" + }, + "interval": { + "default": 60, + "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "type": "number" + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "redirectUri": { + "default": "http://localhost:9078/deezer/callback", + "description": "deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "required": [ + "clientId", + "clientSecret", + "redirectUri" + ], + "type": "object" + }, + "DeezerSourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/DeezerData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "deezer" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "JellySourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "properties": { + "servers": { + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "type": "string" + }, + "user": { + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "type": "string" + } + }, + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "jellyfin" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "LastFmSouceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "configureAs": { + "enum": [ + "source" + ], + "type": "string" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "$ref": "#/definitions/LastfmData" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "lastfm" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "LastfmClientAIOConfig": { + "properties": { + "configureAs": { + "enum": [ + "client", + "source" + ], + "type": "string" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonClientData" + }, + { + "$ref": "#/definitions/LastfmData" + } + ] + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + }, + "type": { + "enum": [ + "lastfm" + ], + "type": "string" + } + }, + "required": [ + "data", + "name", + "type" + ], + "type": "object" + }, + "LastfmData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" + }, + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" + } + }, + "required": [ + "apiKey", + "secret" + ], + "type": "object" + }, + "MalojaClientAIOConfig": { + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonClientData" + }, + { + "properties": { + "apiKey": { + "description": "API Key for Maloja server", + "type": "string" + }, + "url": { + "description": "URL for maloja server", + "type": "string" + } + }, + "required": [ + "apiKey", + "url" + ], + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + }, + "type": { + "enum": [ + "maloja" + ], + "type": "string" + } + }, + "required": [ + "data", + "name", + "type" + ], + "type": "object" + }, + "MatchLoggingOptions": { + "properties": { + "confidenceBreakdown": { + "type": "boolean" + }, + "onMatch": { + "type": "boolean" + }, + "onNoMatch": { + "type": "boolean" + } + }, + "type": "object" + }, + "PlexSourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "properties": { + "libraries": { + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", + "type": "string" + }, + "servers": { + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "type": "string" + }, + "user": { + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "type": "string" + } + }, + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "plex" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "Record": { + "type": "object" + }, + "RequestRetryOptions": { + "properties": { + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "SourceRetryOptions": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "SpotifySourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/SpotifySourceData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "spotify" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "SpotifySourceData": { + "properties": { + "clientId": { + "description": "spotify client id", + "type": "string" + }, + "clientSecret": { + "description": "spotify client secret", + "type": "string" + }, + "interval": { + "default": 60, + "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "type": "number" + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "redirectUri": { + "default": "http://localhost:9078/callback", + "description": "spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "required": [ + "clientId", + "clientSecret", + "redirectUri" + ], + "type": "object" + }, + "SubsonicData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "password": { + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "url": { + "description": "URL of the subsonic media server to query", + "type": "string" + }, + "user": { + "type": "string" + } + }, + "required": [ + "password", + "url", + "user" + ], + "type": "object" + }, + "SubsonicSourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/SubsonicData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "subsonic" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "TautulliSourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "properties": { + "libraries": { + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", + "type": "string" + }, + "servers": { + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "type": "string" + }, + "user": { + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "type": "string" + } + }, + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "tautulli" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + } + }, + "properties": { + "clientDefaults": { + "$ref": "#/definitions/RequestRetryOptions" + }, + "clients": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/MalojaClientAIOConfig" + }, + { + "$ref": "#/definitions/LastfmClientAIOConfig" + } + ] + }, + "type": "array" + }, + "sourceDefaults": { + "$ref": "#/definitions/SourceRetryOptions" + }, + "sources": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/SpotifySourceAIOConfig" + }, + { + "$ref": "#/definitions/PlexSourceAIOConfig" + }, + { + "$ref": "#/definitions/TautulliSourceAIOConfig" + }, + { + "$ref": "#/definitions/DeezerSourceAIOConfig" + }, + { + "$ref": "#/definitions/SubsonicSourceAIOConfig" + }, + { + "$ref": "#/definitions/JellySourceAIOConfig" + }, + { + "$ref": "#/definitions/LastFmSouceAIOConfig" + } + ] + }, + "type": "array" + } + }, + "type": "object" +} + diff --git a/src/index.ts b/src/index.ts index 9bf2bb45..6aaf28af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,10 +11,6 @@ import passport from 'passport'; import session from 'express-session'; import {Writable} from 'stream'; import 'winston-daily-rotate-file'; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'form... Remove this comment to see the full error message -import formidable from 'formidable'; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'conc... Remove this comment to see the full error message -import concatStream from 'concat-stream'; import { buildTrackString, capitalize, createLabelledLogger, @@ -23,15 +19,20 @@ import { readJson, sleep, truncateStringToLength } from "./utils.js"; -import Clients from './clients/ScrobbleClients.js'; -import ScrobbleSources from "./sources/ScrobbleSources.js"; -import {makeClientCheckMiddle, makeSourceCheckMiddle} from "./server/middleware.js"; -import TautulliSource from "./sources/TautulliSource.js"; -import PlexSource, {plexRequestMiddle} from "./sources/PlexSource.js"; -import JellyfinSource from "./sources/JellyfinSource.js"; + +import ScrobbleSources from "./sources/ScrobbleSources"; +import {makeClientCheckMiddle, makeSourceCheckMiddle} from "./server/middleware"; +import TautulliSource from "./sources/TautulliSource"; +import PlexSource, {plexRequestMiddle} from "./sources/PlexSource"; +import JellyfinSource from "./sources/JellyfinSource"; import { Server } from "socket.io"; import path from "path"; -import {projectDir} from "./common/index.js"; +import {projectDir} from "./common/index"; +import LastfmApiClient from "./apis/LastfmApiClient"; +import LastfmSource from "./sources/LastfmSource"; +import LastfmScrobbler from "./clients/LastfmScrobbler"; +import ScrobbleClients from "./clients/ScrobbleClients.js"; + dayjs.extend(utc) dayjs.extend(isBetween); @@ -42,7 +43,9 @@ const app = addAsync(express()); const router = Router(); const port = process.env.PORT ?? 9078; -// @ts-expect-error TS(1378): Top-level 'await' expressions are only allowed whe... Remove this comment to see the full error message + +(async function () { + const server = await app.listen(port) const io = new Server(server); @@ -115,7 +118,7 @@ const logger = winston.loggers.get('default'); const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`); -(async function () { + try { // try to read a configuration file let config = {}; @@ -125,45 +128,16 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) logger.warn('App config file exists but could not be parsed!'); } - // setup defaults for other configs and general config - const { - // @ts-expect-error TS(2339): Property 'spotify' does not exist on type '{}'. - spotify, - // @ts-expect-error TS(2339): Property 'plex' does not exist on type '{}'. - plex, - } = config || {}; - /* * setup clients * */ - const scrobbleClients = new Clients(configDir); + const scrobbleClients = new ScrobbleClients(configDir); await scrobbleClients.buildClientsFromConfig(); if (scrobbleClients.clients.length === 0) { logger.warn('No scrobble clients were configured!') } const scrobbleSources = new ScrobbleSources(localUrl, configDir); - let deprecatedConfigs = []; - if (spotify !== undefined) { - logger.warn(`DEPRECATED: Using 'spotify' top-level property in config.json will be removed in next major version (0.4). Please use 'sources' instead.`) - deprecatedConfigs.push({ - type: 'spotify', - name: 'unnamed', - source: 'config.json (top level)', - mode: 'single', - data: spotify - }); - } - if (plex !== undefined) { - logger.warn(`DEPRECATED: Using 'plex' top-level property in config.json will be removed in next major version (0.4). Please use 'sources' instead.`) - deprecatedConfigs.push({ - type: 'plex', - name: 'unnamed', - source: 'config.json (top level)', - mode: 'single', - data: plex - }); - } // @ts-expect-error TS(2345): Argument of type '{ type: string; name: string; so... Remove this comment to see the full error message await scrobbleSources.buildSourcesFromConfig(deprecatedConfigs); @@ -174,12 +148,9 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const lastfmSources = scrobbleSources.getByType('lastfm'); const lastfmScrobbles = scrobbleClients.getByType('lastfm'); - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. const scrobblerNames = lastfmScrobbles.map(x => x.name); - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. const nameColl = lastfmSources.filter(x => scrobblerNames.includes(x.name)); if(nameColl.length > 0) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. logger.warn(`Last.FM source and clients have same names [${nameColl.map(x => x.name).join(',')}] -- this may cause issues`); } @@ -280,7 +251,6 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) if (req.body.scrobblerConfig !== undefined) { const source = scrobbleSources.getByName(req.body.scrobblerConfig); if (source !== undefined) { - // @ts-expect-error TS(2339): Property 'type' does not exist on type 'never'. if (source.type !== 'tautulli') { this.logger.warn(`Tautulli event specified a config name but the configured source was not a Tautulli type: ${req.body.scrobblerConfig}`); return res.send('OK'); @@ -510,14 +480,12 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) token } = {} } = req; - let entity = scrobbleClients.getByName(state); + let entity: LastfmScrobbler | LastfmSource | undefined = scrobbleClients.getByName(state) as (LastfmScrobbler | undefined); if(entity === undefined) { - entity = scrobbleSources.getByName(state); + entity = scrobbleSources.getByName(state) as LastfmSource; } try { - // @ts-expect-error TS(2532): Object is possibly 'undefined'. await entity.api.authenticate(token); - // @ts-expect-error TS(2532): Object is possibly 'undefined'. await entity.initialize(); return res.send('OK'); } catch (e) { @@ -553,10 +521,8 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) }); let anyNotReady = false; - // @ts-expect-error TS(2339): Property 'canPoll' does not exist on type 'never'. for (const source of scrobbleSources.sources.filter(x => x.canPoll === true)) { await sleep(1500); // stagger polling by 1.5 seconds so that log messages for each source don't get mixed up - // @ts-expect-error TS(2339): Property 'type' does not exist on type 'never'. switch (source.type) { case 'spotify': // @ts-expect-error TS(2339): Property 'spotifyApi' does not exist on type 'neve... Remove this comment to see the full error message @@ -565,22 +531,17 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) if (source.spotifyApi.getAccessToken() === undefined) { anyNotReady = true; } else { - // @ts-expect-error TS(2339): Property 'poll' does not exist on type 'never'. source.poll(scrobbleClients); } } break; case 'lastfm': - // @ts-expect-error TS(2339): Property 'initialized' does not exist on type 'nev... Remove this comment to see the full error message if(source.initialized === true) { - // @ts-expect-error TS(2339): Property 'poll' does not exist on type 'never'. source.poll(scrobbleClients); } break; default: - // @ts-expect-error TS(2339): Property 'poll' does not exist on type 'never'. if (source.poll !== undefined) { - // @ts-expect-error TS(2339): Property 'poll' does not exist on type 'never'. source.poll(scrobbleClients); } } diff --git a/src/sources/AbstractSource.ts b/src/sources/AbstractSource.ts index c4885559..588eaf00 100644 --- a/src/sources/AbstractSource.ts +++ b/src/sources/AbstractSource.ts @@ -1,27 +1,35 @@ -import dayjs from "dayjs"; -import {buildTrackString, capitalize, createLabelledLogger, sleep} from "../utils.js"; - -export default class AbstractSource { - - name; - type; - identifier; - - config; - clients; - logger; - instantiatedAt; - initialized = false; - requiresAuth = false; - requiresAuthInteraction = false; - authed = false; - - canPoll = false; - polling = false; - pollRetries = 0; - tracksDiscovered = 0; - - constructor(type: any, name: any, config = {}, clients = []) { +import dayjs, {Dayjs} from "dayjs"; +import {buildTrackString, capitalize, createLabelledLogger, sleep} from "../utils"; +import {InternalConfig, PlayObject, SourceType} from "../common/infrastructure/Atomic"; +import {Logger} from "winston"; +import {SourceConfig} from "../common/infrastructure/config/source/sources"; + +export default abstract class AbstractSource { + + name: string; + type: SourceType; + identifier: string; + + config: SourceConfig; + clients: string[]; + logger: Logger; + instantiatedAt: Dayjs; + initialized: boolean = false; + requiresAuth: boolean = false; + requiresAuthInteraction: boolean = false; + authed: boolean = false; + + localUrl: string; + + configDir: string; + + canPoll: boolean = false; + polling: boolean = false; + pollRetries: number = 0; + tracksDiscovered: number = 0; + + constructor(type: SourceType, name: string, config: SourceConfig, internal: InternalConfig) { + const {clients = [] } = config; this.type = type; this.name = name; this.identifier = `Source - ${capitalize(this.type)} - ${name}`; @@ -29,6 +37,8 @@ export default class AbstractSource { this.config = config; this.clients = clients; this.instantiatedAt = dayjs(); + this.localUrl = internal.localUrl; + this.configDir = internal.configDir; } // default init function, should be overridden if init stage is required @@ -42,7 +52,7 @@ export default class AbstractSource { return this.authed; } - getRecentlyPlayed = async (options = {}) => { + getRecentlyPlayed = async (options = {}): Promise => { return []; } @@ -66,11 +76,11 @@ export default class AbstractSource { this.pollRetries = 0; const { - // @ts-expect-error TS(2339): Property 'maxPollRetries' does not exist on type '... Remove this comment to see the full error message - maxPollRetries = 0, - // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message - retryMultiplier = 1.5, - } = this.config; + data: { + maxPollRetries = 0, + retryMultiplier = 1.5, + } = {}, + } = this.config.data; // can't have negative retries! const maxRetries = Math.max(0, maxPollRetries); @@ -110,7 +120,7 @@ export default class AbstractSource { this.logger.info('Stopped polling due to user input'); break; } - let playObjs = []; + let playObjs: PlayObject[] = []; this.logger.debug('Refreshing recently played') playObjs = await this.getRecentlyPlayed({formatted: true}); checkCount++; @@ -123,7 +133,8 @@ export default class AbstractSource { if(this.recentlyPlayedTrackIsValid(playObj)) { const {data: { playDate - }: any = {}} = playObj; + } = {} + } = playObj; // @ts-expect-error TS(2339): Property 'unix' does not exist on type 'never'. if (playDate.unix() > lastTrackPlayedAt.unix()) { newTracksFound = true; @@ -135,7 +146,6 @@ export default class AbstractSource { } return { - // @ts-expect-error TS(2698): Spread types may only be created from object types... Remove this comment to see the full error message plays: [...acc.plays, {...playObj, meta: {...playObj.meta, newFromSource: true}}], lastTrackPlayedAt: playDate } diff --git a/src/sources/DeezerSource.ts b/src/sources/DeezerSource.ts index 886db704..2d8397e2 100644 --- a/src/sources/DeezerSource.ts +++ b/src/sources/DeezerSource.ts @@ -1,16 +1,14 @@ -// @ts-expect-error TS(7016): Could not find a declaration file for module 'supe... Remove this comment to see the full error message import request from 'superagent'; -import {parseRetryAfterSecsFromObj, readJson, sleep, sortByPlayDate, writeFile} from "../utils.js"; +import {parseRetryAfterSecsFromObj, readJson, sleep, sortByPlayDate, writeFile} from "../utils"; // @ts-expect-error TS(7016): Could not find a declaration file for module 'pass... Remove this comment to see the full error message import {Strategy as DeezerStrategy} from 'passport-deezer'; -import AbstractSource from "./AbstractSource.js"; +import AbstractSource from "./AbstractSource"; import dayjs from "dayjs"; +import {DeezerSourceConfig} from "../common/infrastructure/config/source/deezer"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; export default class DeezerSource extends AbstractSource { - - localUrl; workingCredsPath; - configDir; error: any; requiresAuth = true; @@ -18,15 +16,14 @@ export default class DeezerSource extends AbstractSource { baseUrl = 'https://api.deezer.com'; - constructor(name: any, config = {}, clients = []) { - super('deezer', name, config, clients); + declare config: DeezerSourceConfig; + + constructor(name: any, config: DeezerSourceConfig, internal: InternalConfig) { + super('deezer', name, config, internal); const { - // @ts-expect-error TS(2339): Property 'localUrl' does not exist on type '{}'. - localUrl, - // @ts-expect-error TS(2339): Property 'configDir' does not exist on type '{}'. - configDir, - // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. - interval = 60, + data: { + interval = 60, + } = {}, } = config; if (interval < 15) { @@ -36,13 +33,11 @@ export default class DeezerSource extends AbstractSource { // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. this.config.interval = interval; - this.configDir = configDir; - this.workingCredsPath = `${configDir}/currentCreds-${name}.json`; - this.localUrl = localUrl; + this.workingCredsPath = `${this.configDir}/currentCreds-${name}.json`; this.canPoll = true; } - static formatPlayObj(obj: any, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false): PlayObject { const { title: name, artist: { diff --git a/src/sources/JellyfinSource.ts b/src/sources/JellyfinSource.ts index f54c2614..4470a0e8 100644 --- a/src/sources/JellyfinSource.ts +++ b/src/sources/JellyfinSource.ts @@ -1,6 +1,8 @@ -import MemorySource from "./MemorySource.js"; +import MemorySource from "./MemorySource"; import dayjs from "dayjs"; -import {buildTrackString, parseDurationFromTimestamp} from "../utils.js"; +import {buildTrackString, parseDurationFromTimestamp} from "../utils"; +import {JellySourceConfig} from "../common/infrastructure/config/source/jellyfin"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; export default class JellyfinSource extends MemorySource { @@ -11,9 +13,11 @@ export default class JellyfinSource extends MemorySource { seenServers = {}; - constructor(name: any, config: any, clients: any, type = 'jellyfin') { - super(type, name, config, clients); - const {users, servers} = config + declare config: JellySourceConfig; + + constructor(name: any, config: JellySourceConfig, internal: InternalConfig) { + super('jellyfin', name, config, internal); + const {data: {users, servers} = {}} = config; if (users === undefined || users === null) { this.users = undefined; @@ -45,7 +49,7 @@ export default class JellyfinSource extends MemorySource { this.initialized = true; } - static formatPlayObj(obj: any, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false): PlayObject { const { ServerId, ServerName, diff --git a/src/sources/LastfmSource.ts b/src/sources/LastfmSource.ts index f06259c5..ff15481c 100644 --- a/src/sources/LastfmSource.ts +++ b/src/sources/LastfmSource.ts @@ -1,6 +1,9 @@ -import AbstractSource from "./AbstractSource.js"; -import LastfmApiClient from "../apis/LastfmApiClient.js"; -import {sortByPlayDate} from "../utils.js"; +import AbstractSource from "./AbstractSource"; +import LastfmApiClient from "../apis/LastfmApiClient"; +import {sortByPlayDate} from "../utils"; +import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; +import {UserGetRecentTracksResponse} from "lastfm-node-client"; export default class LastfmSource extends AbstractSource { @@ -8,13 +11,15 @@ export default class LastfmSource extends AbstractSource { requiresAuth = true; requiresAuthInteraction = true; - constructor(name: any, config = {}, clients = []) { - super('lastfm', name, config, clients); + declare config: LastfmClientConfig; + + constructor(name: any, config: LastfmClientConfig, internal: InternalConfig) { + super('lastfm', name, config, internal); this.canPoll = true; - this.api = new LastfmApiClient(name, config); + this.api = new LastfmApiClient(name, config.data); } - static formatPlayObj(obj: any) { + static formatPlayObj(obj: any): PlayObject { return LastfmApiClient.formatPlayObj(obj); } @@ -38,7 +43,7 @@ export default class LastfmSource extends AbstractSource { getRecentlyPlayed = async(options = {}) => { // @ts-expect-error TS(2339): Property 'limit' does not exist on type '{}'. const {limit = 20, formatted = false} = options; - const resp = await this.api.callApi((client: any) => client.userGetRecentTracks({user: this.api.user, limit, extended: true})); + const resp = await this.api.callApi((client: any) => client.userGetRecentTracks({user: this.api.user, limit, extended: true})); const { recenttracks: { track: list = [], diff --git a/src/sources/MemorySource.ts b/src/sources/MemorySource.ts index 9b5368e8..9ec3dbf7 100644 --- a/src/sources/MemorySource.ts +++ b/src/sources/MemorySource.ts @@ -1,5 +1,5 @@ import AbstractSource from "./AbstractSource.js"; -import {playObjDataMatch, sortByPlayDate, buildTrackString} from "../utils.js"; +import {playObjDataMatch, sortByPlayDate, buildTrackString} from "../utils"; import dayjs from "dayjs"; export default class MemorySource extends AbstractSource { diff --git a/src/sources/PlexSource.ts b/src/sources/PlexSource.ts index 190157be..00d4e623 100644 --- a/src/sources/PlexSource.ts +++ b/src/sources/PlexSource.ts @@ -1,12 +1,10 @@ -// @ts-expect-error TS(7016): Could not find a declaration file for module 'last... Remove this comment to see the full error message -import dayjs from "dayjs";import LastFm from "lastfm-node-client"; -import LastfmScrobbler from '../clients/LastfmScrobbler.js'; -import {buildTrackString, createLabelledLogger} from "../utils.js"; -import AbstractSource from "./AbstractSource.js"; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'form... Remove this comment to see the full error message +import dayjs from "dayjs"; +import {buildTrackString, createLabelledLogger} from "../utils"; +import AbstractSource from "./AbstractSource"; import formidable from 'formidable'; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'conc... Remove this comment to see the full error message import concatStream from 'concat-stream'; +import {PlexSourceConfig} from "../common/infrastructure/config/source/plex"; +import {InternalConfig, PlayObject, SourceType} from "../common/infrastructure/Atomic"; export default class PlexSource extends AbstractSource { // @ts-expect-error TS(7022): 'users' implicitly has type 'any' because it does ... Remove this comment to see the full error message @@ -16,9 +14,11 @@ export default class PlexSource extends AbstractSource { // @ts-expect-error TS(7022): 'servers' implicitly has type 'any' because it doe... Remove this comment to see the full error message servers; - constructor(name: any, config: any, clients: any, type = 'plex') { - super(type, name, config, clients); - const {user, libraries, servers} = config + declare config: PlexSourceConfig; + + constructor(name: any, config: PlexSourceConfig, internal: InternalConfig, type: SourceType = 'plex') { + super(type, name, config, internal); + const {data: {user, libraries, servers} = {}} = config if (user === undefined || user === null) { this.users = undefined; @@ -61,7 +61,7 @@ export default class PlexSource extends AbstractSource { this.initialized = true; } - static formatPlayObj(obj: any, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false): PlayObject { const { event, Account: { @@ -200,6 +200,8 @@ export const plexRequestMiddle = () => { const form = formidable({ allowEmptyFiles: true, multiples: true, + // issue with typings https://github.com/node-formidable/formidable/issues/821 + // @ts-ignore fileWriteStreamHandler: (file: any) => { return concatStream((data: any) => { file.buffer = data; diff --git a/src/sources/ScrobbleSources.ts b/src/sources/ScrobbleSources.ts index 8f009062..ff8cbe77 100644 --- a/src/sources/ScrobbleSources.ts +++ b/src/sources/ScrobbleSources.ts @@ -1,44 +1,61 @@ -import {createLabelledLogger, isValidConfigStructure, readJson} from "../utils.js"; -import SpotifySource from "./SpotifySource.js"; -import PlexSource from "./PlexSource.js"; -import TautulliSource from "./TautulliSource.js"; -import {SubsonicSource} from "./SubsonicSource.js"; -import JellyfinSource from "./JellyfinSource.js"; -import LastfmSource from "./LastfmSource.js"; -import DeezerSource from "./DeezerSource.js"; +import {createLabelledLogger, isValidConfigStructure, readJson, validateJson} from "../utils"; +import SpotifySource from "./SpotifySource"; +import PlexSource from "./PlexSource"; +import TautulliSource from "./TautulliSource"; +import {SubsonicSource} from "./SubsonicSource"; +import JellyfinSource from "./JellyfinSource"; +import LastfmSource from "./LastfmSource"; +import DeezerSource from "./DeezerSource"; +import {ConfigMeta, InternalConfig, SourceType, sourceTypes} from "../common/infrastructure/Atomic"; +import {configDir as defaultConfigDir} from "../common"; +import {Logger} from "winston"; +import {SourceAIOConfig, SourceConfig} from "../common/infrastructure/config/source/sources"; +import {DeezerData, DeezerSourceConfig} from "../common/infrastructure/config/source/deezer"; +import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm"; +import {JellySourceConfig} from "../common/infrastructure/config/source/jellyfin"; +import {SubsonicData, SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic"; +import {TautulliSourceConfig} from "../common/infrastructure/config/source/tautulli"; +import {PlexSourceConfig} from "../common/infrastructure/config/source/plex"; +import {SpotifySourceConfig, SpotifySourceData} from "../common/infrastructure/config/source/spotify"; +import AbstractSource from "./AbstractSource"; +import {AIOConfig} from "../common/infrastructure/config/aioConfig"; +import * as aioSchema from "../common/schema/aio.json"; +import {CommonSourceData} from "../common/infrastructure/config/source"; +import {ClientConfig} from "../common/infrastructure/config/client/clients"; +import * as clientSchema from "../common/schema/client.json"; +import {LastfmSourceConfig} from "../common/infrastructure/config/source/lastfm"; -export default class ScrobbleSources { +type groupedNamedConfigs = {[key: string]: ParsedConfig[]}; + +type ParsedConfig = SourceAIOConfig & ConfigMeta; - sources = []; - logger; - configDir; - localUrl; +export default class ScrobbleSources { - sourceTypes = ['spotify', 'plex', 'tautulli', 'subsonic', 'jellyfin', 'lastfm', 'deezer']; + sources: AbstractSource[] = []; + logger: Logger; + configDir: string; + localUrl: string; - constructor(localUrl: any, configDir = process.cwd()) { + constructor(localUrl: string, configDir: string = defaultConfigDir) { this.configDir = configDir; this.localUrl = localUrl; this.logger = createLabelledLogger('sources', 'Sources'); } getByName = (name: any) => { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. return this.sources.find(x => x.name === name); } getByType = (type: any) => { - // @ts-expect-error TS(2339): Property 'type' does not exist on type 'never'. return this.sources.filter(x => x.type === type); } - getByNameAndType = (name: any, type: any) => { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. + getByNameAndType = (name: string, type: SourceType) => { return this.sources.find(x => x.name === name && x.type === type); } - buildSourcesFromConfig = async (additionalConfigs = []) => { - let configs = additionalConfigs; + buildSourcesFromConfig = async (additionalConfigs: ParsedConfig[] = []) => { + let configs: ParsedConfig[] = additionalConfigs; let configFile; try { @@ -48,12 +65,13 @@ export default class ScrobbleSources { } let sourceDefaults = {}; if (configFile !== undefined) { + const aioConfig = validateJson(configFile, aioSchema, this.logger); const { sources: mainConfigSourcesConfigs = [], sourceDefaults: sd = {}, - } = configFile; + } = aioConfig; sourceDefaults = sd; - const validMainConfigs = mainConfigSourcesConfigs.reduce((acc: any, curr: any, i: any) => { +/* const validMainConfigs = mainConfigSourcesConfigs.reduce((acc: any, curr: any, i: any) => { if(curr === null) { this.logger.error(`The source config entry at index ${i} in config.json is null but should be an object, will not parse`); return acc; @@ -63,21 +81,18 @@ export default class ScrobbleSources { return acc; } return acc.concat(curr); - }, []); - for (const c of validMainConfigs) { + }, []);*/ + for (const c of mainConfigSourcesConfigs) { const {name = 'unnamed'} = c; configs.push({...c, - // @ts-expect-error TS(2322): Type 'any' is not assignable to type 'never'. name, - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'config.json', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. configureAs: 'source' // override user value }); } } - for (let sourceType of this.sourceTypes) { + for (let sourceType of sourceTypes) { let defaultConfigureAs = 'source'; // env builder for single user mode switch (sourceType) { @@ -89,18 +104,14 @@ export default class ScrobbleSources { redirectUri: process.env.SPOTIFY_REDIRECT_URI, refreshToken: process.env.SPOTIFY_REFRESH_TOKEN, }; - if (!Object.values(s).every(x => x === undefined)) { + if (!Object.values(s).every(x => x === undefined && x !== null)) { configs.push({ - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'spotify', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', - // @ts-expect-error TS(2322): Type '{ accessToken: string | undefined; clientId:... Remove this comment to see the full error message - data: s + configureAs: defaultConfigureAs, + data: s as SpotifySourceData }) } break; @@ -111,15 +122,11 @@ export default class ScrobbleSources { }; if (!Object.values(t).every(x => x === undefined)) { configs.push({ - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'tautulli', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', - // @ts-expect-error TS(2322): Type '{ user: string | undefined; }' is not assign... Remove this comment to see the full error message + configureAs: defaultConfigureAs, data: t }) } @@ -130,15 +137,11 @@ export default class ScrobbleSources { }; if (!Object.values(p).every(x => x === undefined)) { configs.push({ - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'plex', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', - // @ts-expect-error TS(2322): Type '{ user: string | undefined; }' is not assign... Remove this comment to see the full error message + configureAs: defaultConfigureAs, data: p }) } @@ -151,16 +154,12 @@ export default class ScrobbleSources { }; if (!Object.values(sub).every(x => x === undefined)) { configs.push({ - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'subsonic', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', - // @ts-expect-error TS(2322): Type '{ user: string | undefined; password: string... Remove this comment to see the full error message - data: sub + configureAs: defaultConfigureAs, + data: sub as SubsonicData }) } break; @@ -171,15 +170,11 @@ export default class ScrobbleSources { }; if (!Object.values(j).every(x => x === undefined)) { configs.push({ - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. type: 'jellyfin', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. name: 'unnamed', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. source: 'ENV', - // @ts-expect-error TS(2322): Type 'string' is not assignable to type 'never'. mode: 'single', - // @ts-expect-error TS(2322): Type '{ user: string | undefined; server: string |... Remove this comment to see the full error message + configureAs: defaultConfigureAs, data: j }) } @@ -189,12 +184,19 @@ export default class ScrobbleSources { defaultConfigureAs = 'client'; break; case 'deezer': - const d = { - clientId: process.env.DEEZER_APP_ID, - clientSecret: process.env.DEEZER_SECRET_KEY, - redirectUri: process.env.DEEZER_REDIRECT_URI, - accessToken: process.env.DEEZER_ACCESS_TOKEN, - }; + configs.push({ + type: 'deezer', + name: 'unnamed', + source: 'ENV', + mode: 'single', + configureAs: defaultConfigureAs, + data: { + clientId: process.env.DEEZER_APP_ID, + clientSecret: process.env.DEEZER_SECRET_KEY, + redirectUri: process.env.DEEZER_REDIRECT_URI, + accessToken: process.env.DEEZER_ACCESS_TOKEN, + } as DeezerData + }); break; default: break; @@ -207,46 +209,38 @@ export default class ScrobbleSources { continue; } if (rawSourceConfigs !== undefined) { - let sourceConfigs = []; + let sourceConfigs: ParsedConfig[] = []; if (Array.isArray(rawSourceConfigs)) { sourceConfigs = rawSourceConfigs; } else if (rawSourceConfigs === null) { this.logger.error(`${sourceType}.json contained no data`); continue; - } else if (typeof rawSourceConfigs === 'object') { - // backwards compatibility, assuming its single-user mode - this.logger.warn(`DEPRECATED: Starting in 0.4 configurations in all [type].json files (${sourceType}.json) must be in an array.`); - if (rawSourceConfigs.data === undefined) { - sourceConfigs = [{data: rawSourceConfigs, mode: 'single', name: 'unnamed'}]; - } else { - sourceConfigs = [rawSourceConfigs]; - } } else { this.logger.error(`All top level data from ${sourceType}.json must be an array of objects, will not parse configs from file`); continue; } - for (const [i,m] of sourceConfigs.entries()) { - if(m === null) { - this.logger.error(`The config entry at index ${i} from ${sourceType}.json is null`); - continue; - } - if (typeof m !== 'object') { - this.logger.error(`The config entry at index ${i} from ${sourceType}.json was not an object, skipping`, m); - continue; - } - const {configureAs = defaultConfigureAs} = m; - if(configureAs === 'source') { - m.source = `${sourceType}.json`; - m.type = sourceType; - // @ts-expect-error TS(2345): Argument of type 'any' is not assignable to parame... Remove this comment to see the full error message - configs.push(m); + for (const [i,rawConf] of sourceConfigs.entries()) { + try { + const validConfig = validateJson(rawConf, clientSchema, this.logger); + + if(sourceType !== 'lastfm' || ((validConfig as LastfmSourceConfig).configureAs === 'source')) { + // @ts-ignore + const parsedConfig: ParsedConfig = { + ...rawConf, + source: `${sourceType}.json`, + type: sourceType + } + configs.push(parsedConfig); + } + } catch (e: any) { + this.logger.error(`The config entry at index ${i} from ${sourceType}.json was not valid`); } } } } // we have all possible configurations so we'll check they are minimally valid - const validConfigs = configs.reduce((acc, c) => { +/* const validConfigs = configs.reduce((acc, c) => { const isValid = isValidConfigStructure(c, {type: true, data: true}); if (isValid !== true) { // @ts-expect-error TS(2339): Property 'source' does not exist on type 'never'. @@ -254,18 +248,17 @@ export default class ScrobbleSources { return acc; } return acc.concat(c); - }, []); + }, []);*/ // finally! all configs are valid, structurally, and can now be passed to addClient // do a last check that names (within each type) are unique and warn if not, but add anyways - const typeGroupedConfigs = validConfigs.reduce((acc, curr) => { + const typeGroupedConfigs = configs.reduce((acc: groupedNamedConfigs, curr: ParsedConfig) => { const {type} = curr; const {[type]: t = []} = acc; return {...acc, [type]: [...t, curr]}; }, {}); // only need to warn if dup names PER TYPE for (const [type, typedConfigs] of Object.entries(typeGroupedConfigs)) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. const nameGroupedConfigs = typedConfigs.reduce((acc: any, curr: any) => { const {name = 'unnamed'} = curr; const {[name]: n = []} = acc; @@ -302,44 +295,45 @@ export default class ScrobbleSources { } addSource = async (clientConfig: any, defaults = {}) => { - const isValidConfig = isValidConfigStructure(clientConfig, {name: true, data: true, type: true}); - if (isValidConfig !== true) { - throw new Error(`Config object from ${clientConfig.source || 'unknown'} with name [${clientConfig.name || 'unnamed'}] of type [${clientConfig.type || 'unknown'}] has errors: ${isValidConfig.join(' | ')}`) - } - const {type, name, clients = [], data: d = {}} = clientConfig; + // const isValidConfig = isValidConfigStructure(clientConfig, {name: true, data: true, type: true}); + // if (isValidConfig !== true) { + // throw new Error(`Config object from ${clientConfig.source || 'unknown'} with name [${clientConfig.name || 'unnamed'}] of type [${clientConfig.type || 'unknown'}] has errors: ${isValidConfig.join(' | ')}`) + // } + + const internal: InternalConfig = { + localUrl: this.localUrl, + configDir: this.configDir + }; + + const {type, name, data: d = {}} = clientConfig; + // add defaults const data = {...defaults, ...d}; + const compositeConfig: SourceConfig = {...clientConfig, data}; + this.logger.debug(`(${name}) Constructing ${type} source`); let newSource; switch (type) { case 'spotify': - newSource = new SpotifySource(name, { - ...data, - localUrl: this.localUrl, - configDir: this.configDir - }, clients); + newSource = new SpotifySource(name, compositeConfig as SpotifySourceConfig, internal); break; case 'plex': - newSource = await new PlexSource(name, data, clients); + newSource = await new PlexSource(name, compositeConfig as PlexSourceConfig, internal); break; case 'tautulli': - newSource = await new TautulliSource(name, data, clients); + newSource = await new TautulliSource(name, compositeConfig as TautulliSourceConfig, internal); break; case 'subsonic': - newSource = new SubsonicSource(name, data, clients); + newSource = new SubsonicSource(name, compositeConfig as SubSonicSourceConfig, internal); break; case 'jellyfin': - newSource = await new JellyfinSource(name, data, clients); + newSource = await new JellyfinSource(name, compositeConfig as JellySourceConfig, internal); break; case 'lastfm': - newSource = await new LastfmSource(name, {...data, configDir: this.configDir}, clients); + newSource = await new LastfmSource(name, compositeConfig as LastfmClientConfig, internal); break; case 'deezer': - newSource = await new DeezerSource(name, { - ...data, - localUrl: this.localUrl, - configDir: this.configDir - }, clients); + newSource = await new DeezerSource(name, compositeConfig as DeezerSourceConfig, internal); break; default: break; @@ -376,7 +370,6 @@ export default class ScrobbleSources { } } - // @ts-expect-error TS(2345): Argument of type 'SpotifySource | PlexSource | Sub... Remove this comment to see the full error message this.sources.push(newSource); } } diff --git a/src/sources/SpotifySource.ts b/src/sources/SpotifySource.ts index de850a34..19fd3bc0 100644 --- a/src/sources/SpotifySource.ts +++ b/src/sources/SpotifySource.ts @@ -3,10 +3,12 @@ import { readJson, writeFile, sortByPlayDate, sleep, parseRetryAfterSecsFromObj, -} from "../utils.js"; +} from "../utils"; // @ts-expect-error TS(7016): Could not find a declaration file for module 'spot... Remove this comment to see the full error message import SpotifyWebApi from "spotify-web-api-node"; -import AbstractSource from "./AbstractSource.js"; +import AbstractSource from "./AbstractSource"; +import {SpotifySourceConfig} from "../common/infrastructure/config/source/spotify"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; const scopes = ['user-read-recently-played', 'user-read-currently-playing']; const state = 'random'; @@ -14,22 +16,19 @@ const state = 'random'; export default class SpotifySource extends AbstractSource { spotifyApi: any; - localUrl; - workingCredsPath; - configDir; + workingCredsPath: string; requiresAuth = true; requiresAuthInteraction = true; - constructor(name: any, config = {}, clients = []) { - super('spotify', name, config, clients); + declare config: SpotifySourceConfig; + + constructor(name: any, config: SpotifySourceConfig, internal: InternalConfig) { + super('spotify', name, config, internal); const { - // @ts-expect-error TS(2339): Property 'localUrl' does not exist on type '{}'. - localUrl, - // @ts-expect-error TS(2339): Property 'configDir' does not exist on type '{}'. - configDir, - // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. - interval = 60, + data: { + interval = 60, + } = {} } = config; if (interval < 15) { @@ -39,13 +38,11 @@ export default class SpotifySource extends AbstractSource { // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. this.config.interval = interval; - this.configDir = configDir; - this.workingCredsPath = `${configDir}/currentCreds-${name}.json`; - this.localUrl = localUrl; + this.workingCredsPath = `${this.configDir}/currentCreds-${name}.json`; this.canPoll = true; } - static formatPlayObj(obj: any, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false): PlayObject { const { track: { artists = [], diff --git a/src/sources/SubsonicSource.ts b/src/sources/SubsonicSource.ts index d5d822a6..64585e5f 100644 --- a/src/sources/SubsonicSource.ts +++ b/src/sources/SubsonicSource.ts @@ -1,11 +1,11 @@ -import AbstractSource from "./AbstractSource.js"; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'supe... Remove this comment to see the full error message import request from 'superagent'; import crypto from 'crypto'; import dayjs from "dayjs"; import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js"; -import {buildTrackString, parseRetryAfterSecsFromObj, sleep} from "../utils.js"; -import MemorySource from "./MemorySource.js"; +import {buildTrackString, parseRetryAfterSecsFromObj, sleep} from "../utils"; +import MemorySource from "./MemorySource"; +import {SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; dayjs.extend(isSameOrAfter); @@ -13,13 +13,23 @@ export class SubsonicSource extends MemorySource { requiresAuth = true; - constructor(name: any, config = {}, clients = []) { + declare config: SubSonicSourceConfig; + + constructor(name: any, config: SubSonicSourceConfig, internal: InternalConfig) { // default to quick interval so we can get a decently accurate nowPlaying - const subsonicConfig = {interval: 10, maxSleep: 30, ...config}; - super('subsonic', name, subsonicConfig, clients); + const { + data: { + // @ts-ignore + interval = 10, + // @ts-ignore + maxSleep = 30, + ...restData + } = {} + } = config; + const subsonicConfig = {...config, data: {...restData, internal, maxSleep}}; + super('subsonic', name, subsonicConfig, internal); - // @ts-expect-error TS(2339): Property 'user' does not exist on type '{}'. - const {user, password, url} = this.config; + const {data: {user, password, url} = {}} = this.config; if (user === undefined) { throw new Error(`Cannot setup Subsonic source, 'user' is not defined`); @@ -34,7 +44,7 @@ export class SubsonicSource extends MemorySource { this.canPoll = true; } - static formatPlayObj(obj: any, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false): PlayObject { const { id, title, diff --git a/src/sources/TautulliSource.ts b/src/sources/TautulliSource.ts index 71c7fb50..29e17a29 100644 --- a/src/sources/TautulliSource.ts +++ b/src/sources/TautulliSource.ts @@ -1,14 +1,16 @@ import dayjs from "dayjs"; -import PlexSource from "./PlexSource.js"; +import PlexSource from "./PlexSource"; +import {TautulliSourceConfig} from "../common/infrastructure/config/source/tautulli"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; -// @ts-expect-error TS(2417): Class static side 'typeof TautulliSource' incorrec... Remove this comment to see the full error message export default class TautulliSource extends PlexSource { - constructor(name: any, config: any, clients: any) { - super(name, config, clients, 'tautulli'); + declare config: TautulliSourceConfig; + constructor(name: any, config: TautulliSourceConfig, internal: InternalConfig) { + super(name, config, internal, 'tautulli'); } - static formatPlayObj(obj: any, newFromSource = false) { + static formatPlayObj(obj: any, newFromSource = false): PlayObject { const { artist_name, track_name, diff --git a/src/utils.ts b/src/utils.ts index f24f21bb..59bf8a39 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,12 +1,12 @@ import fs, {promises, constants} from "fs"; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc.js'; -import winston from "winston"; +import winston, {Logger} from "winston"; import jsonStringify from 'safe-stable-stringify'; // @ts-expect-error TS(7016): Could not find a declaration file for module 'spot... Remove this comment to see the full error message import { TimeoutError, WebapiError } from "spotify-web-api-node/src/response-error.js"; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'supe... Remove this comment to see the full error message import { Response } from 'superagent'; +import Ajv, {Schema} from 'ajv'; const {format} = winston; const {combine, printf, timestamp, label, splat, errors} = format; @@ -178,7 +178,7 @@ export const labelledFormat = (labelName = 'App') => { ); } -export const createLabelledLogger = (name = 'default', label = 'App') => { +export const createLabelledLogger = (name = 'default', label = 'App'): Logger => { if (winston.loggers.has(name)) { return winston.loggers.get(name); } @@ -301,6 +301,7 @@ export const parseRetryAfterSecsFromObj = (err: any) => { if (err instanceof TimeoutError) { return undefined; } + // @ts-ignore if (err instanceof WebapiError || err instanceof Response) { const {headers = {}} = err; raVal = headers['retry-after'] @@ -361,17 +362,14 @@ export const spreadDelay = (retries: any, multiplier: any) => { return s; } -export const removeUndefinedKeys = (obj: any) => { - let newObj = {}; +export const removeUndefinedKeys = >(obj: T): T | undefined => { + let newObj: any = {}; Object.keys(obj).forEach((key) => { if(Array.isArray(obj[key])) { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newObj[key] = obj[key]; } else if (obj[key] === Object(obj[key])) { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newObj[key] = removeUndefinedKeys(obj[key]); } else if (obj[key] !== undefined) { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newObj[key] = obj[key]; } }); @@ -379,12 +377,11 @@ export const removeUndefinedKeys = (obj: any) => { return undefined; } Object.keys(newObj).forEach(key => { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message if(newObj[key] === undefined || (null !== newObj[key] && typeof newObj[key] === 'object' && Object.keys(newObj[key]).length === 0)) { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message delete newObj[key] } }); + //Object.keys(newObj).forEach(key => newObj[key] === undefined || newObj[key] && delete newObj[key]) return newObj; } @@ -429,3 +426,61 @@ export const parseDurationFromTimestamp = (timestamp: any) => { milliseconds: Number.parseInt(milli) }); } + +export const createAjvFactory = (logger: Logger): Ajv => { + const validator = new Ajv({logger: logger, verbose: true, strict: "log", allowUnionTypes: true}); + // https://ajv.js.org/strict-mode.html#unknown-keywords + validator.addKeyword('deprecationMessage'); + return validator; +} + +export const validateJson = (config: object, schema: Schema, logger: Logger): T => { + const ajv = createAjvFactory(logger); + const valid = ajv.validate(schema, config); + if (valid) { + return config as unknown as T; + } else { + logger.error('Json config was not valid. Please use schema to check validity.', {leaf: 'Config'}); + if (Array.isArray(ajv.errors)) { + for (const err of ajv.errors) { + let parts = [ + `At: ${err.dataPath}`, + ]; + let data; + if (typeof err.data === 'string') { + data = err.data; + } else if (err.data !== null && typeof err.data === 'object' && (err.data as any).name !== undefined) { + data = `Object named '${(err.data as any).name}'`; + } + if (data !== undefined) { + parts.push(`Data: ${data}`); + } + let suffix = ''; + // @ts-ignore + if (err.params.allowedValues !== undefined) { + // @ts-ignore + suffix = err.params.allowedValues.join(', '); + suffix = ` [${suffix}]`; + } + parts.push(`${err.keyword}: ${err.schemaPath} => ${err.message}${suffix}`); + + // if we have a reference in the description parse it out so we can log it here for context + if (err.parentSchema !== undefined && err.parentSchema.description !== undefined) { + const desc = err.parentSchema.description as string; + const seeIndex = desc.indexOf('[See]'); + if (seeIndex !== -1) { + let newLineIndex: number | undefined = desc.indexOf('\n', seeIndex); + if (newLineIndex === -1) { + newLineIndex = undefined; + } + const seeFragment = desc.slice(seeIndex + 5, newLineIndex); + parts.push(`See:${seeFragment}`); + } + } + + logger.error(`Schema Error:\r\n${parts.join('\r\n')}`, {leaf: 'Config'}); + } + } + throw new Error('Config schema validity failure'); + } +} diff --git a/tsconfig.json b/tsconfig.json index e88849ec..e05b30fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,9 +5,12 @@ "resolveJsonModule": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, + + "module": "NodeNext", "typeRoots": [ "./node_modules/@types", - ] + "./src/common/infrastructure/typings" + ], }, "include": [ "**/*.ts", -- 2.51.2 From a659897d37a45fcb15e3598c449b00ddf2064a2e Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 12:50:37 -0500 Subject: [PATCH 20/64] refactor: Update project to ESM * Update relative import extension * Remove (bad?) TSconfig extends presets * Fix default non-relative imports --- .gitignore | 5 +- src/apis/AbstractApiClient.ts | 4 +- src/apis/LastfmApiClient.ts | 7 +- src/clients/AbstractScrobbleClient.ts | 10 +- src/clients/LastfmScrobbler.ts | 20 +- src/clients/MalojaScrobbler.ts | 17 +- src/clients/ScrobbleClients.ts | 24 +- src/common/index.ts | 7 +- src/common/infrastructure/config/aioConfig.ts | 8 +- .../infrastructure/config/client/clients.ts | 4 +- .../infrastructure/config/client/index.ts | 2 +- .../infrastructure/config/client/lastfm.ts | 2 +- .../infrastructure/config/client/maloja.ts | 2 +- .../infrastructure/config/source/deezer.ts | 2 +- .../infrastructure/config/source/index.ts | 2 +- .../infrastructure/config/source/jellyfin.ts | 2 +- .../infrastructure/config/source/lastfm.ts | 4 +- .../infrastructure/config/source/plex.ts | 2 +- .../infrastructure/config/source/sources.ts | 14 +- .../infrastructure/config/source/spotify.ts | 2 +- .../infrastructure/config/source/subsonic.ts | 2 +- .../infrastructure/config/source/tautulli.ts | 2 +- src/common/schema/client.json | 155 +++++++ src/common/schema/source.json | 437 ++++++++++++++++++ src/index.ts | 24 +- src/sources/AbstractSource.ts | 11 +- src/sources/DeezerSource.ts | 13 +- src/sources/JellyfinSource.ts | 13 +- src/sources/LastfmSource.ts | 13 +- src/sources/MemorySource.ts | 5 +- src/sources/PlexSource.ts | 11 +- src/sources/ScrobbleSources.ts | 50 +- src/sources/SpotifySource.ts | 13 +- src/sources/SubsonicSource.ts | 15 +- src/sources/TautulliSource.ts | 6 +- src/utils.ts | 12 +- tsconfig.json | 2 +- 37 files changed, 731 insertions(+), 193 deletions(-) create mode 100644 src/common/schema/client.json create mode 100644 src/common/schema/source.json diff --git a/.gitignore b/.gitignore index 0c0c8cff..b7868577 100644 --- a/.gitignore +++ b/.gitignore @@ -122,4 +122,7 @@ dist .idea/ !tsconfig.json -!./src/schema/*.json +!src/common/infrastructure/schema/*.json + +src/**/**.js +src/**/**.js.map diff --git a/src/apis/AbstractApiClient.ts b/src/apis/AbstractApiClient.ts index 48939f1e..792e26f1 100644 --- a/src/apis/AbstractApiClient.ts +++ b/src/apis/AbstractApiClient.ts @@ -1,6 +1,6 @@ -import {capitalize, createLabelledLogger} from "../utils"; +import {capitalize, createLabelledLogger} from "../utils.js"; import {Logger} from "winston"; -import {PlayObject} from "../common/infrastructure/Atomic"; +import {PlayObject} from "../common/infrastructure/Atomic.js"; export default abstract class AbstractApiClient { name: string; diff --git a/src/apis/LastfmApiClient.ts b/src/apis/LastfmApiClient.ts index 9b531915..c128998b 100644 --- a/src/apis/LastfmApiClient.ts +++ b/src/apis/LastfmApiClient.ts @@ -1,9 +1,9 @@ import LastFm, {AuthGetSessionResponse, TrackObject, UserGetInfoResponse} from "lastfm-node-client"; import AbstractApiClient from "./AbstractApiClient.js"; import dayjs from "dayjs"; -import {readJson, sleep, writeFile} from "../utils"; -import {PlayObject} from "../common/infrastructure/Atomic"; -import {LastfmData} from "../common/infrastructure/config/client/lastfm"; +import {readJson, sleep, writeFile} from "../utils.js"; +import {PlayObject} from "../common/infrastructure/Atomic.js"; +import {LastfmData} from "../common/infrastructure/config/client/lastfm.js"; const badErrors = [ 'api key suspended', @@ -89,7 +89,6 @@ export default class LastfmApiClient extends AbstractApiClient { return await func(this.client) as T; } catch (e) { const { - // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message message, } = e; // for now check for exceptional errors by matching error code text diff --git a/src/clients/AbstractScrobbleClient.ts b/src/clients/AbstractScrobbleClient.ts index f2ddad3d..bdce4d8f 100644 --- a/src/clients/AbstractScrobbleClient.ts +++ b/src/clients/AbstractScrobbleClient.ts @@ -1,9 +1,9 @@ import dayjs, {Dayjs} from "dayjs"; -import {buildTrackString, capitalize, createLabelledLogger, playObjDataMatch} from "../utils"; -import {ClientType, INITIALIZED, INITIALIZING, InitState, NOT_INITIALIZED} from "../common/infrastructure/Atomic"; +import {buildTrackString, capitalize, createLabelledLogger, playObjDataMatch} from "../utils.js"; +import {ClientType, INITIALIZED, INITIALIZING, InitState, NOT_INITIALIZED} from "../common/infrastructure/Atomic.js"; import {Logger} from "winston"; -import {CommonClientConfig} from "../common/infrastructure/config/client"; -import {ClientConfig} from "../common/infrastructure/config/client/clients"; +import {CommonClientConfig} from "../common/infrastructure/config/client/index.js"; +import {ClientConfig} from "../common/infrastructure/config/client/clients.js"; export default abstract class AbstractScrobbleClient { @@ -140,7 +140,6 @@ export default abstract class AbstractScrobbleClient { } addScrobbledTrack = (playObj: any, scrobbleResp: any) => { - // @ts-expect-error TS(2322): Type 'any' is not assignable to type 'never'. this.scrobbledPlayObjs.push({play: playObj, scrobble: this.formatPlayObj(scrobbleResp)}); } @@ -167,7 +166,6 @@ export default abstract class AbstractScrobbleClient { } = {} } = playObj; - // @ts-expect-error TS(2339): Property 'play' does not exist on type 'never'. const dtInvariantMatches = this.scrobbledPlayObjs.filter(x => playObjDataMatch(playObj, x.play)); if (dtInvariantMatches.length === 0) { diff --git a/src/clients/LastfmScrobbler.ts b/src/clients/LastfmScrobbler.ts index 22761c2b..fa2facc7 100644 --- a/src/clients/LastfmScrobbler.ts +++ b/src/clients/LastfmScrobbler.ts @@ -1,4 +1,4 @@ -import AbstractScrobbleClient from "./AbstractScrobbleClient"; +import AbstractScrobbleClient from "./AbstractScrobbleClient.js"; import dayjs from 'dayjs'; import { @@ -7,15 +7,15 @@ import { setIntersection, sleep, sortByPlayDate, truncateStringToLength, -} from "../utils"; -import LastfmApiClient from "../apis/LastfmApiClient"; -import {INITIALIZING} from "../common/infrastructure/Atomic"; -import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm"; +} from "../utils.js"; +import LastfmApiClient from "../apis/LastfmApiClient.js"; +import {INITIALIZING} from "../common/infrastructure/Atomic.js"; +import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm.js"; import {TrackScrobbleResponse, UserGetRecentTracksResponse} from "lastfm-node-client"; export default class LastfmScrobbler extends AbstractScrobbleClient { - api; + api: LastfmApiClient; requiresAuth = true; requiresAuthInteraction = true; @@ -81,7 +81,6 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { } return acc.concat(formatted); } catch (e) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. this.logger.warn('Failed to format Last.fm recently scrobbled track, omitting from time frame check', {error: e.message}); this.logger.debug('Full api response object:'); this.logger.debug(x); @@ -94,7 +93,6 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { this.newestScrobbleTime = newestScrobbleTime; this.oldestScrobbleTime = oldestScrobbleTime; - // @ts-expect-error TS(2339): Property 'play' does not exist on type 'never'. this.scrobbledPlayObjs = this.scrobbledPlayObjs.filter(x => this.timeFrameIsValid(x.play)); } } @@ -136,7 +134,6 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { // if we have an submitted play with matching data and play date then we can just return the response from the original scrobble if (existingExactSubmitted !== undefined) { - // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never[... Remove this comment to see the full error message existingScrobble = existingExactSubmitted.scrobble; closestMatch = { @@ -160,7 +157,6 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { // we have have found an existing submission but without an exact date // in which case we can check the scrobble api response against recent scrobbles (also from api) for a more accurate comparison - // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never'... Remove this comment to see the full error message const referenceApiScrobbleResponse = existingDataSubmitted.length > 0 ? existingDataSubmitted[0].scrobble : undefined; const { @@ -216,7 +212,6 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { let artistMatch; const lowerSourceArtists = sourceArtists.map((x: any) => x.toLocaleLowerCase()); - // @ts-expect-error TS(2339): Property 'toLocaleLowerCase' does not exist on typ... Remove this comment to see the full error message const lowerScrobbleArtists = artists.map(x => x.toLocaleLowerCase()); artistMatch = setIntersection(new Set(lowerScrobbleArtists), new Set(lowerSourceArtists)).size / artists.length; @@ -309,14 +304,11 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { } = {}, scrobble: { track: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message '#text': trackName, } = {}, timestamp, ignoredMessage: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message code: ignoreCode, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message '#text': ignoreMsg, } = {}, ...rest diff --git a/src/clients/MalojaScrobbler.ts b/src/clients/MalojaScrobbler.ts index 72dc22ad..36ccb4c1 100644 --- a/src/clients/MalojaScrobbler.ts +++ b/src/clients/MalojaScrobbler.ts @@ -1,4 +1,4 @@ -import AbstractScrobbleClient from "./AbstractScrobbleClient"; +import AbstractScrobbleClient from "./AbstractScrobbleClient.js"; import request from 'superagent'; import dayjs from 'dayjs'; import compareVersions from 'compare-versions'; @@ -10,9 +10,9 @@ import { sortByPlayDate, truncateStringToLength, parseRetryAfterSecsFromObj -} from "../utils"; -import {INITIALIZING} from "../common/infrastructure/Atomic"; -import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja"; +} from "../utils.js"; +import {INITIALIZING} from "../common/infrastructure/Atomic.js"; +import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja.js"; const feat = ["ft.", "ft", "feat.", "feat", "featuring", "Ft.", "Ft", "Feat.", "Feat", "Featuring"]; @@ -113,7 +113,6 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { formatPlayObj = (obj: any) => MalojaScrobbler.formatPlayObj(obj, this.serverVersion); - // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message callApi = async (req: any, retries = 0) => { const { // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message @@ -132,9 +131,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { return await this.callApi(req, retries + 1) } const { - // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message message, - // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response: { // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status, @@ -143,7 +140,6 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message text, } = {}, - // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response, } = e; let msg = response !== undefined ? `API Call failed: Server Response => ${message}` : `API Call failed: ${message}`; @@ -268,7 +264,6 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { }); } } catch (e) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. if(e.status === 403) { // may be an older version that doesn't support auth readiness before db upgrade // and if it was before api was accessible during db build then test would fail during testConnection() @@ -326,7 +321,6 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { this.newestScrobbleTime = newestScrobbleTime; this.oldestScrobbleTime = oldestScrobbleTime; - // @ts-expect-error TS(2339): Property 'play' does not exist on type 'never'. this.scrobbledPlayObjs = this.scrobbledPlayObjs.filter(x => this.timeFrameIsValid(x.play)); } } @@ -382,7 +376,6 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { // if we have an submitted play with matching data and play date then we can just return the response from the original scrobble if (existingExactSubmitted !== undefined) { - // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never[... Remove this comment to see the full error message existingScrobble = existingExactSubmitted.scrobble; closestMatch = { @@ -406,7 +399,6 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { // we have have found an existing submission but without an exact date // in which case we can check the scrobble api response against recent scrobbles (also from api) for a more accurate comparison - // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type 'never'... Remove this comment to see the full error message const referenceApiScrobbleResponse = existingDataSubmitted.length > 0 ? existingDataSubmitted[0].scrobble : undefined; const { @@ -462,7 +454,6 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { let artistMatch; const lowerSourceArtists = sourceArtists.map((x: any) => x.toLocaleLowerCase()); - // @ts-expect-error TS(2339): Property 'toLocaleLowerCase' does not exist on typ... Remove this comment to see the full error message const lowerScrobbleArtists = artists.map(x => x.toLocaleLowerCase()); artistMatch = setIntersection(new Set(lowerScrobbleArtists), new Set(lowerSourceArtists)).size / artists.length; diff --git a/src/clients/ScrobbleClients.ts b/src/clients/ScrobbleClients.ts index 34bae35b..296f4429 100644 --- a/src/clients/ScrobbleClients.ts +++ b/src/clients/ScrobbleClients.ts @@ -6,16 +6,16 @@ import { playObjDataMatch, readJson, returnDuplicateStrings, validateJson -} from "../utils"; -import MalojaScrobbler from "./MalojaScrobbler"; -import LastfmScrobbler from "./LastfmScrobbler"; -import {clientTypes, ConfigMeta} from "../common/infrastructure/Atomic"; -import {AIOConfig} from "../common/infrastructure/config/aioConfig"; -import * as aioSchema from '../common/schema/aio.json'; -import * as clientSchema from '../common/schema/client.json'; -import {ClientAIOConfig, ClientConfig} from "../common/infrastructure/config/client/clients"; -import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja"; -import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm"; +} from "../utils.js"; +import MalojaScrobbler from "./MalojaScrobbler.js"; +import LastfmScrobbler from "./LastfmScrobbler.js"; +import {clientTypes, ConfigMeta} from "../common/infrastructure/Atomic.js"; +import {AIOConfig} from "../common/infrastructure/config/aioConfig.js"; +import * as aioSchema from '../common/schema/aio.json' assert {type: "json"}; +import * as clientSchema from '../common/schema/client.json' assert {type: "json"}; +import {ClientAIOConfig, ClientConfig} from "../common/infrastructure/config/client/clients.js"; +import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja.js"; +import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm.js"; type groupedNamedConfigs = {[key: string]: ParsedConfig[]}; @@ -93,6 +93,7 @@ export default class ScrobbleClients { name: 'unnamed', source: 'ENV', mode: 'single', + configureAs: 'client', data: { url, // @ts-ignore @@ -114,6 +115,7 @@ export default class ScrobbleClients { name: 'unnamed', source: 'ENV', mode: 'single', + configureAs: 'client', // @ts-ignore data: lfm }) @@ -348,7 +350,6 @@ ${sources.join('\n')}`); client.tracksScrobbled++; // since this is what we return to the source only add to tracksScrobbled if not already in array // (source should only know that a track was scrobbled (binary) -- doesn't care if it was scrobbled more than once - // @ts-expect-error TS(7006): Parameter 'x' implicitly has an 'any' type. if(!tracksScrobbled.some(x => playObjDataMatch(x, playObj) && x.data.playDate === playObj.data.playDate)) { tracksScrobbled.push(playObj); } @@ -357,7 +358,6 @@ ${sources.join('\n')}`); this.logger.error(`Encountered error while in scrobble loop for ${client.name}`); this.logger.error(e); // for now just stop scrobbling plays for this client and move on. the client should deal with logging the issue - // @ts-expect-error TS(2571): Object is of type 'unknown'. if(e.continueScrobbling !== true) { break; } diff --git a/src/common/index.ts b/src/common/index.ts index b302a63e..fa45b338 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,7 +1,8 @@ -import path from 'path'; +import * as path from 'path'; +import {fileURLToPath} from "url"; -// const __filename = fileURLToPath(import.meta.url); -// const __dirname = path.dirname(__filename); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); export const projectDir = path.resolve(__dirname, '../../'); export const configDir: string = path.resolve(projectDir, './config'); diff --git a/src/common/infrastructure/config/aioConfig.ts b/src/common/infrastructure/config/aioConfig.ts index d1c5ffca..665ab70c 100644 --- a/src/common/infrastructure/config/aioConfig.ts +++ b/src/common/infrastructure/config/aioConfig.ts @@ -1,7 +1,7 @@ -import {SourceRetryOptions} from "./source"; -import {RequestRetryOptions} from "./common"; -import {SourceAIOConfig} from "./source/sources"; -import {ClientAIOConfig} from "./client/clients"; +import {SourceRetryOptions} from "./source/index.js"; +import {RequestRetryOptions} from "./common.js"; +import {SourceAIOConfig} from "./source/sources.js"; +import {ClientAIOConfig} from "./client/clients.js"; export interface AIOConfig { sourceDefaults?: SourceRetryOptions diff --git a/src/common/infrastructure/config/client/clients.ts b/src/common/infrastructure/config/client/clients.ts index d4151a46..bc4ce0c2 100644 --- a/src/common/infrastructure/config/client/clients.ts +++ b/src/common/infrastructure/config/client/clients.ts @@ -1,5 +1,5 @@ -import {MalojaClientAIOConfig, MalojaClientConfig} from "./maloja"; -import {LastfmClientAIOConfig, LastfmClientConfig} from "./lastfm"; +import {MalojaClientAIOConfig, MalojaClientConfig} from "./maloja.js"; +import {LastfmClientAIOConfig, LastfmClientConfig} from "./lastfm.js"; export type ClientConfig = MalojaClientConfig | LastfmClientConfig; diff --git a/src/common/infrastructure/config/client/index.ts b/src/common/infrastructure/config/client/index.ts index 8607fc5b..4b662934 100644 --- a/src/common/infrastructure/config/client/index.ts +++ b/src/common/infrastructure/config/client/index.ts @@ -1,4 +1,4 @@ -import {CommonConfig, CommonData} from "../common"; +import {CommonConfig, CommonData} from "../common.js"; export interface MatchLoggingOptions { onNoMatch?: boolean diff --git a/src/common/infrastructure/config/client/lastfm.ts b/src/common/infrastructure/config/client/lastfm.ts index 79cc5a22..f9aea172 100644 --- a/src/common/infrastructure/config/client/lastfm.ts +++ b/src/common/infrastructure/config/client/lastfm.ts @@ -1,4 +1,4 @@ -import {CommonClientConfig, CommonClientData} from "./index"; +import {CommonClientConfig, CommonClientData} from "./index.js"; export interface LastfmData { /** diff --git a/src/common/infrastructure/config/client/maloja.ts b/src/common/infrastructure/config/client/maloja.ts index 35afdc97..d0f267ea 100644 --- a/src/common/infrastructure/config/client/maloja.ts +++ b/src/common/infrastructure/config/client/maloja.ts @@ -1,4 +1,4 @@ -import {CommonClientConfig, CommonClientData} from "./index"; +import {CommonClientConfig, CommonClientData} from "./index.js"; export interface MalojaClientConfig extends CommonClientConfig { data: CommonClientData & { diff --git a/src/common/infrastructure/config/source/deezer.ts b/src/common/infrastructure/config/source/deezer.ts index 017db12a..e117aa0c 100644 --- a/src/common/infrastructure/config/source/deezer.ts +++ b/src/common/infrastructure/config/source/deezer.ts @@ -1,4 +1,4 @@ -import {CommonSourceConfig, CommonSourceData} from "./index"; +import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface DeezerData extends CommonSourceData { /** diff --git a/src/common/infrastructure/config/source/index.ts b/src/common/infrastructure/config/source/index.ts index cd0f0c4d..666071f1 100644 --- a/src/common/infrastructure/config/source/index.ts +++ b/src/common/infrastructure/config/source/index.ts @@ -1,4 +1,4 @@ -import {CommonConfig, CommonData, RequestRetryOptions} from "../common"; +import {CommonConfig, CommonData, RequestRetryOptions} from "../common.js"; export interface SourceRetryOptions extends RequestRetryOptions { /** diff --git a/src/common/infrastructure/config/source/jellyfin.ts b/src/common/infrastructure/config/source/jellyfin.ts index 4dec0df3..39e23ba2 100644 --- a/src/common/infrastructure/config/source/jellyfin.ts +++ b/src/common/infrastructure/config/source/jellyfin.ts @@ -1,4 +1,4 @@ -import {CommonSourceConfig, CommonSourceData} from "./index"; +import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface JellySourceConfig extends CommonSourceConfig { data: CommonSourceData & { diff --git a/src/common/infrastructure/config/source/lastfm.ts b/src/common/infrastructure/config/source/lastfm.ts index 07e763e4..e2fad00b 100644 --- a/src/common/infrastructure/config/source/lastfm.ts +++ b/src/common/infrastructure/config/source/lastfm.ts @@ -1,5 +1,5 @@ -import {LastfmData} from "../client/lastfm"; -import {CommonSourceConfig, CommonSourceData} from "./index"; +import {LastfmData} from "../client/lastfm.js"; +import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface LastfmSourceConfig extends CommonSourceConfig { configureAs?: 'source' diff --git a/src/common/infrastructure/config/source/plex.ts b/src/common/infrastructure/config/source/plex.ts index f961b7fa..1817f37e 100644 --- a/src/common/infrastructure/config/source/plex.ts +++ b/src/common/infrastructure/config/source/plex.ts @@ -1,4 +1,4 @@ -import {CommonSourceConfig, CommonSourceData} from "./index"; +import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface PlexSourceConfig extends CommonSourceConfig { data: CommonSourceData & { diff --git a/src/common/infrastructure/config/source/sources.ts b/src/common/infrastructure/config/source/sources.ts index 292bfd13..1966fc9d 100644 --- a/src/common/infrastructure/config/source/sources.ts +++ b/src/common/infrastructure/config/source/sources.ts @@ -1,10 +1,10 @@ -import {SpotifySourceAIOConfig, SpotifySourceConfig} from "./spotify"; -import {PlexSourceAIOConfig, PlexSourceConfig} from "./plex"; -import {TautulliSourceAIOConfig, TautulliSourceConfig} from "./tautulli"; -import {DeezerSourceAIOConfig, DeezerSourceConfig} from "./deezer"; -import {SubsonicSourceAIOConfig, SubSonicSourceConfig} from "./subsonic"; -import {JellySourceAIOConfig, JellySourceConfig} from "./jellyfin"; -import {LastFmSouceAIOConfig, LastfmSourceConfig} from "./lastfm"; +import {SpotifySourceAIOConfig, SpotifySourceConfig} from "./spotify.js"; +import {PlexSourceAIOConfig, PlexSourceConfig} from "./plex.js"; +import {TautulliSourceAIOConfig, TautulliSourceConfig} from "./tautulli.js"; +import {DeezerSourceAIOConfig, DeezerSourceConfig} from "./deezer.js"; +import {SubsonicSourceAIOConfig, SubSonicSourceConfig} from "./subsonic.js"; +import {JellySourceAIOConfig, JellySourceConfig} from "./jellyfin.js"; +import {LastFmSouceAIOConfig, LastfmSourceConfig} from "./lastfm.js"; export type SourceConfig = SpotifySourceConfig | PlexSourceConfig | TautulliSourceConfig | DeezerSourceConfig | SubSonicSourceConfig | JellySourceConfig | LastfmSourceConfig; diff --git a/src/common/infrastructure/config/source/spotify.ts b/src/common/infrastructure/config/source/spotify.ts index acbfadcc..410d08aa 100644 --- a/src/common/infrastructure/config/source/spotify.ts +++ b/src/common/infrastructure/config/source/spotify.ts @@ -1,4 +1,4 @@ -import {CommonSourceConfig, CommonSourceData} from "./index"; +import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface SpotifySourceData extends CommonSourceData { /** diff --git a/src/common/infrastructure/config/source/subsonic.ts b/src/common/infrastructure/config/source/subsonic.ts index a8af2e59..4caadb48 100644 --- a/src/common/infrastructure/config/source/subsonic.ts +++ b/src/common/infrastructure/config/source/subsonic.ts @@ -1,4 +1,4 @@ -import {CommonSourceConfig, CommonSourceData} from "./index"; +import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface SubsonicData extends CommonSourceData { /** diff --git a/src/common/infrastructure/config/source/tautulli.ts b/src/common/infrastructure/config/source/tautulli.ts index 32127a55..237d4365 100644 --- a/src/common/infrastructure/config/source/tautulli.ts +++ b/src/common/infrastructure/config/source/tautulli.ts @@ -1,4 +1,4 @@ -import {PlexSourceConfig} from "./plex"; +import {PlexSourceConfig} from "./plex.js"; export interface TautulliSourceConfig extends PlexSourceConfig { } diff --git a/src/common/schema/client.json b/src/common/schema/client.json new file mode 100644 index 00000000..972e08ef --- /dev/null +++ b/src/common/schema/client.json @@ -0,0 +1,155 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "anyOf": [ + { + "$ref": "#/definitions/MalojaClientConfig" + }, + { + "$ref": "#/definitions/LastfmClientConfig" + } + ], + "definitions": { + "CommonClientData": { + "properties": { + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a client can make before error is thrown.", + "type": "number" + }, + "options": { + "properties": { + "checkExistingScrobbles": { + "type": "boolean" + }, + "refreshEnabled": { + "type": "boolean" + }, + "verbose": { + "properties": { + "match": { + "$ref": "#/definitions/MatchLoggingOptions" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying).", + "type": "number" + } + }, + "type": "object" + }, + "LastfmClientConfig": { + "properties": { + "configureAs": { + "enum": [ + "client", + "source" + ], + "type": "string" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonClientData" + }, + { + "$ref": "#/definitions/LastfmData" + } + ] + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + } + }, + "required": [ + "data", + "name" + ], + "type": "object" + }, + "LastfmData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" + }, + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" + } + }, + "required": [ + "apiKey", + "secret" + ], + "type": "object" + }, + "MalojaClientConfig": { + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonClientData" + }, + { + "properties": { + "apiKey": { + "description": "API Key for Maloja server", + "type": "string" + }, + "url": { + "description": "URL for maloja server", + "type": "string" + } + }, + "required": [ + "apiKey", + "url" + ], + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + } + }, + "required": [ + "data", + "name" + ], + "type": "object" + }, + "MatchLoggingOptions": { + "properties": { + "confidenceBreakdown": { + "type": "boolean" + }, + "onMatch": { + "type": "boolean" + }, + "onNoMatch": { + "type": "boolean" + } + }, + "type": "object" + } + } +} + diff --git a/src/common/schema/source.json b/src/common/schema/source.json new file mode 100644 index 00000000..2686c544 --- /dev/null +++ b/src/common/schema/source.json @@ -0,0 +1,437 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "anyOf": [ + { + "$ref": "#/definitions/SpotifySourceConfig" + }, + { + "$ref": "#/definitions/PlexSourceConfig" + }, + { + "$ref": "#/definitions/TautulliSourceConfig" + }, + { + "$ref": "#/definitions/DeezerSourceConfig" + }, + { + "$ref": "#/definitions/SubSonicSourceConfig" + }, + { + "$ref": "#/definitions/JellySourceConfig" + }, + { + "$ref": "#/definitions/LastfmSourceConfig" + } + ], + "definitions": { + "CommonSourceData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "DeezerData": { + "properties": { + "clientId": { + "description": "deezer client id", + "type": "string" + }, + "clientSecret": { + "description": "deezer client secret", + "type": "string" + }, + "interval": { + "default": 60, + "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "type": "number" + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "redirectUri": { + "default": "http://localhost:9078/deezer/callback", + "description": "deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "required": [ + "clientId", + "clientSecret", + "redirectUri" + ], + "type": "object" + }, + "DeezerSourceConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/DeezerData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + }, + "JellySourceConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "properties": { + "servers": { + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "type": "string" + }, + "user": { + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "type": "string" + } + }, + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + }, + "LastfmData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" + }, + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" + } + }, + "required": [ + "apiKey", + "secret" + ], + "type": "object" + }, + "LastfmSourceConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "configureAs": { + "enum": [ + "source" + ], + "type": "string" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "$ref": "#/definitions/LastfmData" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + }, + "PlexSourceConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "properties": { + "libraries": { + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", + "type": "string" + }, + "servers": { + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "type": "string" + }, + "user": { + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "type": "string" + } + }, + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + }, + "Record": { + "type": "object" + }, + "SpotifySourceConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/SpotifySourceData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + }, + "SpotifySourceData": { + "properties": { + "clientId": { + "description": "spotify client id", + "type": "string" + }, + "clientSecret": { + "description": "spotify client secret", + "type": "string" + }, + "interval": { + "default": 60, + "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "type": "number" + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "redirectUri": { + "default": "http://localhost:9078/callback", + "description": "spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "required": [ + "clientId", + "clientSecret", + "redirectUri" + ], + "type": "object" + }, + "SubSonicSourceConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/SubsonicData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + }, + "SubsonicData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "password": { + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "url": { + "description": "URL of the subsonic media server to query", + "type": "string" + }, + "user": { + "type": "string" + } + }, + "required": [ + "password", + "url", + "user" + ], + "type": "object" + }, + "TautulliSourceConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "properties": { + "libraries": { + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", + "type": "string" + }, + "servers": { + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "type": "string" + }, + "user": { + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "type": "string" + } + }, + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + } + } +} + diff --git a/src/index.ts b/src/index.ts index 6aaf28af..716f88b6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,17 +20,17 @@ import { truncateStringToLength } from "./utils.js"; -import ScrobbleSources from "./sources/ScrobbleSources"; -import {makeClientCheckMiddle, makeSourceCheckMiddle} from "./server/middleware"; -import TautulliSource from "./sources/TautulliSource"; -import PlexSource, {plexRequestMiddle} from "./sources/PlexSource"; -import JellyfinSource from "./sources/JellyfinSource"; +import ScrobbleSources from "./sources/ScrobbleSources.js"; +import {makeClientCheckMiddle, makeSourceCheckMiddle} from "./server/middleware.js"; +import TautulliSource from "./sources/TautulliSource.js"; +import PlexSource, {plexRequestMiddle} from "./sources/PlexSource.js"; +import JellyfinSource from "./sources/JellyfinSource.js"; import { Server } from "socket.io"; -import path from "path"; -import {projectDir} from "./common/index"; -import LastfmApiClient from "./apis/LastfmApiClient"; -import LastfmSource from "./sources/LastfmSource"; -import LastfmScrobbler from "./clients/LastfmScrobbler"; +import * as path from "path"; +import {projectDir} from "./common/index.js"; +import LastfmApiClient from "./apis/LastfmApiClient.js"; +import LastfmSource from "./sources/LastfmSource.js"; +import LastfmScrobbler from "./clients/LastfmScrobbler.js"; import ScrobbleClients from "./clients/ScrobbleClients.js"; @@ -445,7 +445,6 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) app.getAsync(/.*deezer\/callback*$/, function (req, res, next) { // @ts-expect-error TS(2339): Property 'deezerSource' does not exist on type 'Se... Remove this comment to see the full error message const entity = scrobbleSources.getByName(req.session.deezerSource); - // @ts-expect-error TS(2532): Object is possibly 'undefined'. const passportFunc = passport.authenticate(`deezer-${entity.name}`, {session: false}); return passportFunc(req, res, next); }, async function (req, res) { @@ -458,7 +457,6 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) // @ts-expect-error TS(2532): Object is possibly 'undefined'. } else if(entity.config.accessToken !== undefined) { // start polling - // @ts-expect-error TS(2532): Object is possibly 'undefined'. entity.poll(entity.clients) return res.redirect('/'); } else { @@ -489,7 +487,6 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) await entity.initialize(); return res.send('OK'); } catch (e) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. return res.send(e.message); } } else { @@ -503,7 +500,6 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const tokenResult = await source.handleAuthCodeCallback(req.query); let responseContent = 'OK'; if (tokenResult === true) { - // @ts-expect-error TS(2532): Object is possibly 'undefined'. source.poll(scrobbleClients); } else { responseContent = tokenResult; diff --git a/src/sources/AbstractSource.ts b/src/sources/AbstractSource.ts index 588eaf00..0c141f82 100644 --- a/src/sources/AbstractSource.ts +++ b/src/sources/AbstractSource.ts @@ -1,8 +1,8 @@ import dayjs, {Dayjs} from "dayjs"; -import {buildTrackString, capitalize, createLabelledLogger, sleep} from "../utils"; -import {InternalConfig, PlayObject, SourceType} from "../common/infrastructure/Atomic"; +import {buildTrackString, capitalize, createLabelledLogger, sleep} from "../utils.js"; +import {InternalConfig, PlayObject, SourceType} from "../common/infrastructure/Atomic.js"; import {Logger} from "winston"; -import {SourceConfig} from "../common/infrastructure/config/source/sources"; +import {SourceConfig} from "../common/infrastructure/config/source/sources.js"; export default abstract class AbstractSource { @@ -128,20 +128,17 @@ export default abstract class AbstractSource { let closeToInterval = false; const now = dayjs(); - // @ts-expect-error TS(2769): No overload matches this call. const playInfo = playObjs.reduce((acc, playObj) => { if(this.recentlyPlayedTrackIsValid(playObj)) { const {data: { playDate } = {} } = playObj; - // @ts-expect-error TS(2339): Property 'unix' does not exist on type 'never'. if (playDate.unix() > lastTrackPlayedAt.unix()) { newTracksFound = true; this.logger.info(`New Track => ${buildTrackString(playObj)}`); if (closeToInterval === false) { - // @ts-expect-error TS(2339): Property 'unix' does not exist on type 'never'. closeToInterval = Math.abs(now.unix() - playDate.unix()) < 5; } @@ -157,9 +154,7 @@ export default abstract class AbstractSource { } return acc; }, {plays: [], lastTrackPlayedAt}); - // @ts-expect-error TS(2339): Property 'plays' does not exist on type 'never'. playObjs = playInfo.plays; - // @ts-expect-error TS(2339): Property 'lastTrackPlayedAt' does not exist on typ... Remove this comment to see the full error message lastTrackPlayedAt = playInfo.lastTrackPlayedAt; if (closeToInterval) { diff --git a/src/sources/DeezerSource.ts b/src/sources/DeezerSource.ts index 2d8397e2..cfb14339 100644 --- a/src/sources/DeezerSource.ts +++ b/src/sources/DeezerSource.ts @@ -1,11 +1,10 @@ import request from 'superagent'; -import {parseRetryAfterSecsFromObj, readJson, sleep, sortByPlayDate, writeFile} from "../utils"; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'pass... Remove this comment to see the full error message +import {parseRetryAfterSecsFromObj, readJson, sleep, sortByPlayDate, writeFile} from "../utils.js"; import {Strategy as DeezerStrategy} from 'passport-deezer'; -import AbstractSource from "./AbstractSource"; +import AbstractSource from "./AbstractSource.js"; import dayjs from "dayjs"; -import {DeezerSourceConfig} from "../common/infrastructure/config/source/deezer"; -import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; +import {DeezerSourceConfig} from "../common/infrastructure/config/source/deezer.js"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; export default class DeezerSource extends AbstractSource { workingCredsPath; @@ -116,7 +115,6 @@ export default class DeezerSource extends AbstractSource { return resp.data; } - // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message callApi = async (req: any, retries = 0) => { const { // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message @@ -158,9 +156,7 @@ export default class DeezerSource extends AbstractSource { return await this.callApi(req, retries + 1) } const { - // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message message, - // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response: { // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status, @@ -181,7 +177,6 @@ export default class DeezerSource extends AbstractSource { // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message text, } = {}, - // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response, } = e; let msg = response !== undefined ? `API Call failed: Server Response => ${ssMessage}` : `API Call failed: ${message}`; diff --git a/src/sources/JellyfinSource.ts b/src/sources/JellyfinSource.ts index 4470a0e8..6d786b03 100644 --- a/src/sources/JellyfinSource.ts +++ b/src/sources/JellyfinSource.ts @@ -1,14 +1,12 @@ -import MemorySource from "./MemorySource"; +import MemorySource from "./MemorySource.js"; import dayjs from "dayjs"; -import {buildTrackString, parseDurationFromTimestamp} from "../utils"; -import {JellySourceConfig} from "../common/infrastructure/config/source/jellyfin"; -import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; +import {buildTrackString, parseDurationFromTimestamp} from "../utils.js"; +import {JellySourceConfig} from "../common/infrastructure/config/source/jellyfin.js"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; export default class JellyfinSource extends MemorySource { - // @ts-expect-error TS(7022): 'users' implicitly has type 'any' because it does ... Remove this comment to see the full error message users; - // @ts-expect-error TS(7022): 'servers' implicitly has type 'any' because it doe... Remove this comment to see the full error message servers; seenServers = {}; @@ -148,9 +146,7 @@ export default class JellyfinSource extends MemorySource { } handle = async (playObj: any, allClients: any) => { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message if(this.seenServers[playObj.meta.server] === undefined) { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message this.seenServers[playObj.meta.server] = playObj.meta.sourceVersion; this.logger.info(`Received data from server ${playObj.meta.server} (Version ${playObj.meta.sourceVersion}) for the first time.`); } @@ -168,7 +164,6 @@ export default class JellyfinSource extends MemorySource { const recent = await this.getRecentlyPlayed(); const newestPlay = recent[recent.length - 1]; try { - // @ts-expect-error TS(2339): Property 'data' does not exist on type 'never'. await allClients.scrobble(newPlays, {scrobbleTo: this.clients, scrobbleFrom: this.identifier, checkTime: newestPlay.data.playDate}); // only gets hit if we scrobbled ok this.tracksDiscovered++; diff --git a/src/sources/LastfmSource.ts b/src/sources/LastfmSource.ts index ff15481c..f35e4258 100644 --- a/src/sources/LastfmSource.ts +++ b/src/sources/LastfmSource.ts @@ -1,13 +1,13 @@ -import AbstractSource from "./AbstractSource"; -import LastfmApiClient from "../apis/LastfmApiClient"; -import {sortByPlayDate} from "../utils"; -import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm"; -import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; +import AbstractSource from "./AbstractSource.js"; +import LastfmApiClient from "../apis/LastfmApiClient.js"; +import {sortByPlayDate} from "../utils.js"; +import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm.js"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; import {UserGetRecentTracksResponse} from "lastfm-node-client"; export default class LastfmSource extends AbstractSource { - api; + api: LastfmApiClient; requiresAuth = true; requiresAuthInteraction = true; @@ -75,7 +75,6 @@ export default class LastfmSource extends AbstractSource { } return acc.concat(formatted); } catch (e) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. this.logger.warn('Failed to format Last.fm recently scrobbled track, omitting from time frame check', {error: e.message}); this.logger.debug('Full api response object:'); this.logger.debug(x); diff --git a/src/sources/MemorySource.ts b/src/sources/MemorySource.ts index 9ec3dbf7..f797be3e 100644 --- a/src/sources/MemorySource.ts +++ b/src/sources/MemorySource.ts @@ -1,5 +1,5 @@ import AbstractSource from "./AbstractSource.js"; -import {playObjDataMatch, sortByPlayDate, buildTrackString} from "../utils"; +import {playObjDataMatch, sortByPlayDate, buildTrackString} from "../utils.js"; import dayjs from "dayjs"; export default class MemorySource extends AbstractSource { @@ -50,7 +50,6 @@ export default class MemorySource extends AbstractSource { for(const candidate of this.candidateRecentlyPlayed) { const {data: {playDate, track}} = candidate; - // @ts-expect-error TS(2339): Property 'isBefore' does not exist on type 'never'... Remove this comment to see the full error message if(playDate.isBefore(dayjs().subtract(30, 's'))) { // a prior candidate has been playing for more than 30 seconds, time to check statefuls @@ -63,10 +62,8 @@ export default class MemorySource extends AbstractSource { } else { const {data: { playDate, duration }} = candidate; const {data: { playDate: rplayDate }} = matchingRecent; - // @ts-expect-error TS(2339): Property 'isSame' does not exist on type 'never'. if(!playDate.isSame(rplayDate)) { if(duration !== undefined) { - // @ts-expect-error TS(2339): Property 'isAfter' does not exist on type 'never'. if(playDate.isAfter(rplayDate.add(duration, 's'))) { this.logger.debug(`${stPrefix} added after being seen for 30 seconds and having a different timestamp than a prior play`); newStatefulPlays.push(candidate); diff --git a/src/sources/PlexSource.ts b/src/sources/PlexSource.ts index 00d4e623..4d5dcb59 100644 --- a/src/sources/PlexSource.ts +++ b/src/sources/PlexSource.ts @@ -1,17 +1,14 @@ import dayjs from "dayjs"; -import {buildTrackString, createLabelledLogger} from "../utils"; -import AbstractSource from "./AbstractSource"; +import {buildTrackString, createLabelledLogger} from "../utils.js"; +import AbstractSource from "./AbstractSource.js"; import formidable from 'formidable'; import concatStream from 'concat-stream'; -import {PlexSourceConfig} from "../common/infrastructure/config/source/plex"; -import {InternalConfig, PlayObject, SourceType} from "../common/infrastructure/Atomic"; +import {PlexSourceConfig} from "../common/infrastructure/config/source/plex.js"; +import {InternalConfig, PlayObject, SourceType} from "../common/infrastructure/Atomic.js"; export default class PlexSource extends AbstractSource { - // @ts-expect-error TS(7022): 'users' implicitly has type 'any' because it does ... Remove this comment to see the full error message users; - // @ts-expect-error TS(7022): 'libraries' implicitly has type 'any' because it d... Remove this comment to see the full error message libraries; - // @ts-expect-error TS(7022): 'servers' implicitly has type 'any' because it doe... Remove this comment to see the full error message servers; declare config: PlexSourceConfig; diff --git a/src/sources/ScrobbleSources.ts b/src/sources/ScrobbleSources.ts index ff8cbe77..5397fa6a 100644 --- a/src/sources/ScrobbleSources.ts +++ b/src/sources/ScrobbleSources.ts @@ -1,29 +1,29 @@ -import {createLabelledLogger, isValidConfigStructure, readJson, validateJson} from "../utils"; -import SpotifySource from "./SpotifySource"; -import PlexSource from "./PlexSource"; -import TautulliSource from "./TautulliSource"; -import {SubsonicSource} from "./SubsonicSource"; -import JellyfinSource from "./JellyfinSource"; -import LastfmSource from "./LastfmSource"; -import DeezerSource from "./DeezerSource"; -import {ConfigMeta, InternalConfig, SourceType, sourceTypes} from "../common/infrastructure/Atomic"; -import {configDir as defaultConfigDir} from "../common"; +import {createLabelledLogger, isValidConfigStructure, readJson, validateJson} from "../utils.js"; +import SpotifySource from "./SpotifySource.js"; +import PlexSource from "./PlexSource.js"; +import TautulliSource from "./TautulliSource.js"; +import {SubsonicSource} from "./SubsonicSource.js"; +import JellyfinSource from "./JellyfinSource.js"; +import LastfmSource from "./LastfmSource.js"; +import DeezerSource from "./DeezerSource.js"; +import {ConfigMeta, InternalConfig, SourceType, sourceTypes} from "../common/infrastructure/Atomic.js"; +import {configDir as defaultConfigDir} from "../common/index.js"; import {Logger} from "winston"; -import {SourceAIOConfig, SourceConfig} from "../common/infrastructure/config/source/sources"; -import {DeezerData, DeezerSourceConfig} from "../common/infrastructure/config/source/deezer"; -import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm"; -import {JellySourceConfig} from "../common/infrastructure/config/source/jellyfin"; -import {SubsonicData, SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic"; -import {TautulliSourceConfig} from "../common/infrastructure/config/source/tautulli"; -import {PlexSourceConfig} from "../common/infrastructure/config/source/plex"; -import {SpotifySourceConfig, SpotifySourceData} from "../common/infrastructure/config/source/spotify"; -import AbstractSource from "./AbstractSource"; -import {AIOConfig} from "../common/infrastructure/config/aioConfig"; -import * as aioSchema from "../common/schema/aio.json"; -import {CommonSourceData} from "../common/infrastructure/config/source"; -import {ClientConfig} from "../common/infrastructure/config/client/clients"; -import * as clientSchema from "../common/schema/client.json"; -import {LastfmSourceConfig} from "../common/infrastructure/config/source/lastfm"; +import {SourceAIOConfig, SourceConfig} from "../common/infrastructure/config/source/sources.js"; +import {DeezerData, DeezerSourceConfig} from "../common/infrastructure/config/source/deezer.js"; +import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm.js"; +import {JellySourceConfig} from "../common/infrastructure/config/source/jellyfin.js"; +import {SubsonicData, SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic.js"; +import {TautulliSourceConfig} from "../common/infrastructure/config/source/tautulli.js"; +import {PlexSourceConfig} from "../common/infrastructure/config/source/plex.js"; +import {SpotifySourceConfig, SpotifySourceData} from "../common/infrastructure/config/source/spotify.js"; +import AbstractSource from "./AbstractSource.js"; +import {AIOConfig} from "../common/infrastructure/config/aioConfig.js"; +import * as aioSchema from "../common/schema/aio.json" assert {type: "json"}; +import {CommonSourceData} from "../common/infrastructure/config/source/index.js"; +import {ClientConfig} from "../common/infrastructure/config/client/clients.js"; +import * as clientSchema from "../common/schema/client.json" assert {type: "json"}; +import {LastfmSourceConfig} from "../common/infrastructure/config/source/lastfm.js"; type groupedNamedConfigs = {[key: string]: ParsedConfig[]}; diff --git a/src/sources/SpotifySource.ts b/src/sources/SpotifySource.ts index 19fd3bc0..5b65e690 100644 --- a/src/sources/SpotifySource.ts +++ b/src/sources/SpotifySource.ts @@ -3,12 +3,11 @@ import { readJson, writeFile, sortByPlayDate, sleep, parseRetryAfterSecsFromObj, -} from "../utils"; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'spot... Remove this comment to see the full error message +} from "../utils.js"; import SpotifyWebApi from "spotify-web-api-node"; -import AbstractSource from "./AbstractSource"; -import {SpotifySourceConfig} from "../common/infrastructure/config/source/spotify"; -import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; +import AbstractSource from "./AbstractSource.js"; +import {SpotifySourceConfig} from "../common/infrastructure/config/source/spotify.js"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; const scopes = ['user-read-recently-played', 'user-read-currently-playing']; const state = 'random'; @@ -214,7 +213,6 @@ export default class SpotifySource extends AbstractSource { return result; } - // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message callApi = async (func: any, retries = 0) => { const { // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message @@ -225,7 +223,6 @@ export default class SpotifySource extends AbstractSource { try { return await func(this.spotifyApi); } catch (e) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. if (e.statusCode === 401) { if (this.spotifyApi.getRefreshToken() === undefined) { throw new Error('Access token was not valid and no refresh token was present') @@ -251,7 +248,6 @@ export default class SpotifySource extends AbstractSource { return await func(this.spotifyApi); } catch (ee) { this.logger.error('Refreshing access token encountered an error'); - // @ts-expect-error TS(2769): No overload matches this call. this.logger.error(ee, {label: 'Spotify'}); throw ee; } @@ -262,7 +258,6 @@ export default class SpotifySource extends AbstractSource { return this.callApi(func, retries + 1); } else { this.logger.error(`Request failed on retry (${retries}) with no more retries permitted (max ${maxRequestRetries})`); - // @ts-expect-error TS(2769): No overload matches this call. this.logger.error(e, {label: 'Spotify'}); throw e; } diff --git a/src/sources/SubsonicSource.ts b/src/sources/SubsonicSource.ts index 64585e5f..7391427a 100644 --- a/src/sources/SubsonicSource.ts +++ b/src/sources/SubsonicSource.ts @@ -1,11 +1,11 @@ import request from 'superagent'; -import crypto from 'crypto'; +import * as crypto from 'crypto'; import dayjs from "dayjs"; import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js"; -import {buildTrackString, parseRetryAfterSecsFromObj, sleep} from "../utils"; -import MemorySource from "./MemorySource"; -import {SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic"; -import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; +import {buildTrackString, parseRetryAfterSecsFromObj, sleep} from "../utils.js"; +import MemorySource from "./MemorySource.js"; +import {SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic.js"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; dayjs.extend(isSameOrAfter); @@ -72,7 +72,6 @@ export class SubsonicSource extends MemorySource { } } - // @ts-expect-error TS(7024): Function implicitly has return type 'any' because ... Remove this comment to see the full error message callApi = async (req: any, retries = 0) => { const { // @ts-expect-error TS(2339): Property 'user' does not exist on type '{}'. @@ -120,9 +119,7 @@ export class SubsonicSource extends MemorySource { return await this.callApi(req, retries + 1) } const { - // @ts-expect-error TS(2339): Property 'message' does not exist on type 'unknown... Remove this comment to see the full error message message, - // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response: { // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message status, @@ -143,7 +140,6 @@ export class SubsonicSource extends MemorySource { // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message text, } = {}, - // @ts-expect-error TS(2339): Property 'response' does not exist on type 'unknow... Remove this comment to see the full error message response, } = e; let msg = response !== undefined ? `API Call failed: Server Response => ${ssMessage}` : `API Call failed: ${message}`; @@ -161,7 +157,6 @@ export class SubsonicSource extends MemorySource { this.logger.info('Subsonic Connection: ok'); this.initialized = true; } catch (e) { - // @ts-expect-error TS(2571): Object is of type 'unknown'. if(e.status !== undefined && e.status !== 404) { this.logger.info('Subsonic Connection: ok'); // we at least got a response! diff --git a/src/sources/TautulliSource.ts b/src/sources/TautulliSource.ts index 29e17a29..744d5dbd 100644 --- a/src/sources/TautulliSource.ts +++ b/src/sources/TautulliSource.ts @@ -1,7 +1,7 @@ import dayjs from "dayjs"; -import PlexSource from "./PlexSource"; -import {TautulliSourceConfig} from "../common/infrastructure/config/source/tautulli"; -import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic"; +import PlexSource from "./PlexSource.js"; +import {TautulliSourceConfig} from "../common/infrastructure/config/source/tautulli.js"; +import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; export default class TautulliSource extends PlexSource { diff --git a/src/utils.ts b/src/utils.ts index 59bf8a39..f2841ad6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,9 +1,8 @@ -import fs, {promises, constants} from "fs"; +import {promises, constants} from "fs"; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc.js'; import winston, {Logger} from "winston"; -import jsonStringify from 'safe-stable-stringify'; -// @ts-expect-error TS(7016): Could not find a declaration file for module 'spot... Remove this comment to see the full error message +import stringify from 'safe-stable-stringify'; import { TimeoutError, WebapiError } from "spotify-web-api-node/src/response-error.js"; import { Response } from 'superagent'; import Ajv, {Schema} from 'ajv'; @@ -20,7 +19,6 @@ export async function readJson(this: any, path: any, {logErrors = true, throwOnN // @ts-expect-error TS(2345): Argument of type 'Buffer' is not assignable to par... Remove this comment to see the full error message return JSON.parse(data); } catch (e) { - // @ts-expect-error TS(2339): Property 'code' does not exist on type 'unknown'. const {code} = e; if (code === 'ENOENT') { if (throwOnNotFound) { @@ -144,7 +142,7 @@ const CWD = process.cwd(); let longestLabel = 3; export const defaultFormat = printf(({level, message, label = 'App', timestamp, [SPLAT]: splatObj, stack, ...rest}) => { - let stringifyValue = splatObj !== undefined ? jsonStringify(splatObj) : ''; + let stringifyValue = splatObj !== undefined ? stringify.default(splatObj) : ''; if (label.length > longestLabel) { longestLabel = label.length; } @@ -427,8 +425,8 @@ export const parseDurationFromTimestamp = (timestamp: any) => { }); } -export const createAjvFactory = (logger: Logger): Ajv => { - const validator = new Ajv({logger: logger, verbose: true, strict: "log", allowUnionTypes: true}); +export const createAjvFactory = (logger: Logger): Ajv.default => { + const validator = new Ajv.default({logger: logger, verbose: true, strict: "log", allowUnionTypes: true}); // https://ajv.js.org/strict-mode.html#unknown-keywords validator.addKeyword('deprecationMessage'); return validator; diff --git a/tsconfig.json b/tsconfig.json index e05b30fc..1013d12e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { - "extends": "@tsconfig/node18/tsconfig.json", "compilerOptions": { "sourceMap": true, "resolveJsonModule": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, + "esModuleInterop": false, "module": "NodeNext", "typeRoots": [ -- 2.51.2 From 861179805168d9b4189464ac53dbae9539ebcfac Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 13:51:06 -0500 Subject: [PATCH 21/64] it works! --- src/clients/MalojaScrobbler.ts | 27 +++++++++------------------ src/clients/ScrobbleClients.ts | 2 +- src/index.ts | 3 +-- src/sources/ScrobbleSources.ts | 29 ++++++++++++++++------------- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/clients/MalojaScrobbler.ts b/src/clients/MalojaScrobbler.ts index 36ccb4c1..d5cdf4d8 100644 --- a/src/clients/MalojaScrobbler.ts +++ b/src/clients/MalojaScrobbler.ts @@ -24,11 +24,9 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { declare config: MalojaClientConfig - constructor(name: any, config: MalojaClientConfig, options = {}) { - // @ts-expect-error TS(2554): Expected 2-3 arguments, but got 4. - super('maloja', name, config, options); - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url, apiKey} = config; + constructor(name: any, config: MalojaClientConfig) { + super('maloja', name, config); + const {url, apiKey} = config.data; if (apiKey === undefined) { this.logger.warn("'apiKey' not found in config! Client will most likely fail when trying to scrobble"); } @@ -115,11 +113,9 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { callApi = async (req: any, retries = 0) => { const { - // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 1, - // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5 - } = this.config; + } = this.config.data; try { return await req; @@ -151,8 +147,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { testConnection = async () => { - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url} = this.config; + const {url} = this.config.data; try { const serverInfoResp = await this.callApi(request.get(`${url}/apis/mlj_1/serverinfo`)); const { @@ -189,8 +184,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { testHealth = async () => { - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url} = this.config; + const {url} = this.config.data; try { // @ts-expect-error TS(2345): Argument of type '{ maxRequestRetries: number; }' ... Remove this comment to see the full error message const serverInfoResp = await this.callApi(request.get(`${url}/apis/mlj_1/serverinfo`), {maxRequestRetries: 0}); @@ -236,8 +230,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { testAuth = async () => { - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url, apiKey} = this.config; + const {url, apiKey} = this.config.data; try { const resp = await this.callApi(request .get(`${url}/apis/mlj_1/test`) @@ -306,8 +299,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { refreshScrobbles = async () => { if (this.refreshEnabled) { this.logger.debug('Refreshing recent scrobbles'); - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url} = this.config; + const {url} = this.config.data; const resp = await this.callApi(request.get(`${url}/apis/mlj_1/scrobbles?max=20`)); const { body: { @@ -496,8 +488,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } scrobble = async (playObj: any) => { - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url, apiKey} = this.config; + const {url, apiKey} = this.config.data; const { data: { diff --git a/src/clients/ScrobbleClients.ts b/src/clients/ScrobbleClients.ts index 296f4429..193e3748 100644 --- a/src/clients/ScrobbleClients.ts +++ b/src/clients/ScrobbleClients.ts @@ -240,7 +240,7 @@ ${sources.join('\n')}`); newClient = new MalojaScrobbler(name, ({...clientConfig, data} as unknown as MalojaClientConfig)); break; case 'lastfm': - newClient = new LastfmScrobbler(name, {...clientConfig, data: {configDir: this.configDir, data} } as unknown as LastfmClientConfig ); + newClient = new LastfmScrobbler(name, {...clientConfig, data: {configDir: this.configDir, ...data} } as unknown as LastfmClientConfig ); break; default: break; diff --git a/src/index.ts b/src/index.ts index 716f88b6..eff850ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -138,8 +138,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) } const scrobbleSources = new ScrobbleSources(localUrl, configDir); - // @ts-expect-error TS(2345): Argument of type '{ type: string; name: string; so... Remove this comment to see the full error message - await scrobbleSources.buildSourcesFromConfig(deprecatedConfigs); + await scrobbleSources.buildSourcesFromConfig(); const clientCheckMiddle = makeClientCheckMiddle(scrobbleClients); const sourceCheckMiddle = makeSourceCheckMiddle(scrobbleSources); diff --git a/src/sources/ScrobbleSources.ts b/src/sources/ScrobbleSources.ts index 5397fa6a..15f554f6 100644 --- a/src/sources/ScrobbleSources.ts +++ b/src/sources/ScrobbleSources.ts @@ -184,19 +184,22 @@ export default class ScrobbleSources { defaultConfigureAs = 'client'; break; case 'deezer': - configs.push({ - type: 'deezer', - name: 'unnamed', - source: 'ENV', - mode: 'single', - configureAs: defaultConfigureAs, - data: { - clientId: process.env.DEEZER_APP_ID, - clientSecret: process.env.DEEZER_SECRET_KEY, - redirectUri: process.env.DEEZER_REDIRECT_URI, - accessToken: process.env.DEEZER_ACCESS_TOKEN, - } as DeezerData - }); + const d = { + clientId: process.env.DEEZER_APP_ID, + clientSecret: process.env.DEEZER_SECRET_KEY, + redirectUri: process.env.DEEZER_REDIRECT_URI, + accessToken: process.env.DEEZER_ACCESS_TOKEN, + }; + if (!Object.values(d).every(x => x === undefined)) { + configs.push({ + type: 'deezer', + name: 'unnamed', + source: 'ENV', + mode: 'single', + configureAs: defaultConfigureAs, + data: d as DeezerData + }); + } break; default: break; -- 2.51.2 From ac2aa8297c7114436fc8310b2e2f283eb24afb5a Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 14:48:12 -0500 Subject: [PATCH 22/64] Remove ts-migrate --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index e78c1969..232e9e11 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,6 @@ "@types/superagent": "^4.1.16", "@types/triple-beam": "^1.3.2", "ts-essentials": "^9.1.2", - "ts-migrate": "^0.1.35", "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", "typescript": "^4.9.5", -- 2.51.2 From 1e9518ac543c1ff07fa2d4c5b350a10a44244854 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 14:48:20 -0500 Subject: [PATCH 23/64] Fix spotify source credential handling --- package-lock.json | 8496 +++++++-------------------------------------- 1 file changed, 1256 insertions(+), 7240 deletions(-) diff --git a/package-lock.json b/package-lock.json index 50ce812a..a1cac397 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,6 @@ "@types/superagent": "^4.1.16", "@types/triple-beam": "^1.3.2", "ts-essentials": "^9.1.2", - "ts-migrate": "^0.1.35", "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", "typescript": "^4.9.5", @@ -57,19 +56,6 @@ "npm": ">=9.3.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@awaitjs/express": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@awaitjs/express/-/express-0.6.3.tgz", @@ -81,6724 +67,1651 @@ "express": "4.x" } }, - "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", - "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", - "dev": true, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "engines": { - "node": ">=6.9.0" + "node": ">=0.1.90" } }, - "node_modules/@babel/core": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": ">=12" } }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@babel/generator": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", - "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", - "dev": true, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", "dependencies": { - "@babel/types": "^7.20.7", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, "engines": { "node": ">=6.0.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", - "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", - "dev": true, - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.18.6", - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", - "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.20.7", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } + "node_modules/@tsconfig/node18": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-1.0.1.tgz", + "integrity": "sha512-sNFeK6X2ATlhlvzyH4kKYQlfHXE2f2/wxtB9ClvYXevWpmwkUT7VaSrjIN9E76Qebz8qP5JOJJ9jD3QoD/Z9TA==", + "dev": true }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", - "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.2.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "node_modules/@types/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" + "@types/node": "*" } }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "@types/node": "*" } }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/ms": { + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cookiejar": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", "dev": true }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", - "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", - "dev": true, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" + "@types/node": "*" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", - "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "node_modules/@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", "dev": true, "dependencies": { - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "node_modules/@types/express-session": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz", + "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" + "@types/express": "*" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "node_modules/@types/formidable": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.5.tgz", + "integrity": "sha512-uvMcdn/KK3maPOaVUAc3HEYbCEhjaGFwww4EsX6IJfWIJ1tzHtDHczuImH3GKdusPnAAmzB07St90uabZeCKPA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" + "@types/node": "*" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", - "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.20.7", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@types/node": { + "version": "18.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", + "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "node_modules/@types/passport": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.11.tgz", + "integrity": "sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" + "@types/express": "*" } }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.0" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true }, - "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", "dev": true, - "engines": { - "node": ">=6.9.0" + "dependencies": { + "@types/mime": "*", + "@types/node": "*" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "node_modules/@types/superagent": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", + "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", "dev": true, - "engines": { - "node": ">=6.9.0" + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } + "node_modules/@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", - "dev": true, + "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==", "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.6" } }, - "node_modules/@babel/helpers": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", - "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.13", - "@babel/types": "^7.20.7" - }, "engines": { - "node": ">=6.9.0" + "node": ">=0.4.0" } }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, + "node_modules/ajv": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.4.tgz", + "integrity": "sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } + "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==" }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "engines": { - "node": ">=4" + "node": "^4.5.0 || >= 5.9" } }, - "node_modules/@babel/parser": { - "version": "7.20.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", - "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", "engines": { "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", - "dev": true, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "bytes": "3.1.2", + "content-type": "~1.0.4", + "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.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", - "dev": true, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.8" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz", - "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==", - "dev": true, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=10" }, - "peerDependencies": { - "@babel/core": "^7.12.0" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dev": true, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "color-convert": "^1.9.3", + "color-string": "^1.6.0" } }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "color-name": "~1.1.4" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=7.0.0" } }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "dev": true, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dev": true, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "color-name": "1.1.3" } }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "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": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=0.1.90" } }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, + "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": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "color": "^3.1.3", + "text-hex": "1.0.x" } }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dev": true, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.8" } }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz", - "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==", - "dev": true, + "node_modules/compare-versions": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.4.tgz", + "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==" + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" } }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "dev": true, + "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==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.6" } }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", - "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.6" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.6" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "object-assign": "^4", + "vary": "^1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 0.10" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=0.4.0" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, + "node_modules/dayjs": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", + "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "ms": "2.0.0" } }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" } }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.8" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, + "node_modules/dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "asap": "^2.0.0", + "wrappy": "1" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=0.3.1" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "jake": "^10.8.5" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "bin": { + "ejs": "bin/cli.js" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-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/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "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==", + "engines": { + "node": ">= 0.8" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, + "node_modules/engine.io": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.0.tgz", + "integrity": "sha512-OgxY1c/RuCSeO/rTr8DIFXx76IzUUft86R7/P7MMbbkuzeqJoTNw2lmeD91IyGz41QYleIIjWeMJGgug043sfQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=10.0.0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=10.0.0" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.6" } }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", - "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", - "dev": true, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "ms": "2.1.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=6.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=6" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.6" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.20.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz", - "integrity": "sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA==", - "dev": true, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.10.0" } }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz", - "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==", - "dev": true, + "node_modules/express-session": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", + "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "globals": "^11.1.0" + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.8.0" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", - "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/template": "^7.20.7" - }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.6" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", - "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", - "dev": true, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "moment": "^2.29.1" } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", - "dev": true, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "minimatch": "^5.0.1" } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", - "dev": true, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "balanced-match": "^1.0.0" } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", - "dev": true, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=10" } }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", - "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", - "dev": true, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-flow": "^7.18.6" + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.8" } }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", - "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "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/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", - "dev": true, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 6" } }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", - "dev": true, + "node_modules/formidable": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", + "integrity": "sha512-GRGDJTWAZ3H+umZbF2bKcqjsTov25zgon1St05ziKdiSw3kxvI+meMJrXx3ylRmuSADOpviSakBuS4yvGCGnSg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" + "dezalgo": "1.0.3", + "hexoid": "1.0.0", + "once": "1.4.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.6" } }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" - }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.6" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", - "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-simple-access": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", - "dev": true, - "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=6.9.0" + "node": "*" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", - "dev": true, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "function-bind": "^1.1.1" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.4.0" } }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", - "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" - }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=8" } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { - "node": ">=6.9.0" + "node": ">= 0.4" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", - "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "regenerator-transform": "^0.15.1" - }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=8" } }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", - "dev": true, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.8" } }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", - "dev": true, + "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==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=0.10.0" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">= 0.10" } }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "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/@babel/plugin-transform-typescript": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.13.tgz", - "integrity": "sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.20.12", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=8" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", - "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, + "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": ">=6.9.0" + "node": ">=8" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", - "dev": true, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" }, - "engines": { - "node": ">=6.9.0" + "bin": { + "jake": "bin/cli.js" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=10" } }, - "node_modules/@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "node_modules/@babel/preset-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.18.6.tgz", - "integrity": "sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-flow-strip-types": "^7.18.6" - }, + "node_modules/lastfm-node-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/lastfm-node-client/-/lastfm-node-client-2.2.0.tgz", + "integrity": "sha512-nhHxrRPaNKIJnEuRov68HGjQRAFel2UWnG86ieQAklaVOdBciC6DO0vajZjzPZGqwvUAxQzDHPsp2ceDOtKW2w==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=8.10.0" } }, - "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dev": true, + "node_modules/logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" } }, - "node_modules/@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - }, + "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/logform/node_modules/safe-stable-stringify": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=10" } }, - "node_modules/@babel/register": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.18.9.tgz", - "integrity": "sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw==", - "dev": true, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { - "clone-deep": "^4.0.1", - "find-cache-dir": "^2.0.0", - "make-dir": "^2.1.0", - "pirates": "^4.0.5", - "source-map-support": "^0.5.16" + "yallist": "^4.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=10" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "node_modules/@babel/runtime": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, + "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==", "engines": { - "node": ">=6.9.0" + "node": ">= 0.6" } }, - "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { - "node": ">=6.9.0" + "node": ">= 0.6" } }, - "node_modules/@babel/traverse": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", - "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.13", - "@babel/types": "^7.20.7", - "debug": "^4.1.0", - "globals": "^11.1.0" + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=4" } }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.6" } }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", - "dev": true, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" + "mime-db": "1.52.0" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "engines": { - "node": ">=0.1.90" + "node": ">= 0.6" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=12" + "node": "*" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" } }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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==", "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 0.6" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" - }, - "node_modules/@ts-morph/bootstrap": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@ts-morph/bootstrap/-/bootstrap-0.16.0.tgz", - "integrity": "sha512-FYW3bK5EBeAgpHu0qZ57gHbLjzgzC81y5EJmrebzIhXSYg6OgZu5lFHpF5NJ7CwM7ZMhxX1PG+DRA8e+skopKw==", - "dev": true, - "dependencies": { - "@ts-morph/common": "~0.16.0" - } - }, - "node_modules/@ts-morph/common": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.16.0.tgz", - "integrity": "sha512-SgJpzkTgZKLKqQniCjLaE3c2L2sdL7UShvmTmPBejAKd2OKV/yfMpQ2IWpAuA+VY5wy7PkSUaEObIqEK6afFuw==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.11", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "path-browserify": "^1.0.1" - } - }, - "node_modules/@ts-morph/common/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "node_modules/@tsconfig/node18": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-1.0.1.tgz", - "integrity": "sha512-sNFeK6X2ATlhlvzyH4kKYQlfHXE2f2/wxtB9ClvYXevWpmwkUT7VaSrjIN9E76Qebz8qP5JOJJ9jD3QoD/Z9TA==", - "dev": true - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, - "node_modules/@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "node_modules/@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.33", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", - "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "node_modules/@types/express-session": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz", - "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/formidable": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.5.tgz", - "integrity": "sha512-uvMcdn/KK3maPOaVUAc3HEYbCEhjaGFwww4EsX6IJfWIJ1tzHtDHczuImH3GKdusPnAAmzB07St90uabZeCKPA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", - "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" - }, - "node_modules/@types/passport": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.11.tgz", - "integrity": "sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "node_modules/@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", - "dev": true, - "dependencies": { - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/superagent": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", - "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", - "dev": true, - "dependencies": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, - "node_modules/@types/triple-beam": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", - "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" - }, - "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==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.4.tgz", - "integrity": "sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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==" - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ast-types": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.7.tgz", - "integrity": "sha512-2mP3TwtkY/aTv5X3ZsMpNAbOnyoC/aMJwJSoaELPkHId0nSQgFcnU4dRW3isxiz7+zBexk0ym3WNVjMiQBnJSw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/babel-core": { - "version": "7.0.0-bridge.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", - "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", - "dev": true, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "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.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/braces/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001451", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz", - "integrity": "sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dev": true, - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", - "dev": true, - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "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/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-versions": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.4.tgz", - "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==" - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "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==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/core-js-compat": { - "version": "3.27.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", - "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/create-jest-runner": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/create-jest-runner/-/create-jest-runner-0.5.3.tgz", - "integrity": "sha512-a9VY2doMBmzRollJB3Ft3/Y5fBceSWJ4gdyVsg4/d7nP1S4715VG939s2VnITDj79YBmRgKhjGjNRv1c+Kre1g==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "jest-worker": "^24.0.0", - "throat": "^4.1.0" - }, - "bin": { - "create-jest-runner": "generator/index.js" - } - }, - "node_modules/create-jest-runner/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/create-jest-runner/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/create-jest-runner/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/create-jest-runner/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/create-jest-runner/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/create-jest-runner/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/create-jest-runner/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "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==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.292", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.292.tgz", - "integrity": "sha512-ESWOSyJy5odDlE8wvh5NNAMORv4r6assPwIPGHEMWrWD0SONXcG/xT+9aD9CQyeRwyYDPo6dJT4Bbeg5uevVQQ==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "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", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/engine.io": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.0.tgz", - "integrity": "sha512-OgxY1c/RuCSeO/rTr8DIFXx76IzUUft86R7/P7MMbbkuzeqJoTNw2lmeD91IyGz41QYleIIjWeMJGgug043sfQ==", - "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-session": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", - "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", - "dependencies": { - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express-session/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/fast-glob/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-stream-rotator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", - "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", - "dependencies": { - "moment": "^2.29.1" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "node_modules/flow-parser": { - "version": "0.199.1", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.199.1.tgz", - "integrity": "sha512-Mt+GFUQYij3miM7Z6o8E3aHTGXZKSOhvlCFgdQRoi6fkWfhyijnoX51zpOxM5PmZuiV6gallWhDZzwOsWxRutg==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "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/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formidable": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", - "integrity": "sha512-GRGDJTWAZ3H+umZbF2bKcqjsTov25zgon1St05ziKdiSw3kxvI+meMJrXx3ylRmuSADOpviSakBuS4yvGCGnSg==", - "dependencies": { - "dezalgo": "1.0.3", - "hexoid": "1.0.0", - "once": "1.4.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", - "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", - "dev": true, - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "engines": { - "node": ">=8" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "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==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.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/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", - "dev": true, - "dependencies": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jscodeshift": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.6.4.tgz", - "integrity": "sha512-+NF/tlNbc2WEhXUuc4WEJLsJumF84tnaMUZW2hyJw3jThKKRvsPX4sPJVgO1lPE28z0gNL+gwniLG9d8mYvQCQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.1.6", - "@babel/parser": "^7.1.6", - "@babel/plugin-proposal-class-properties": "^7.1.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/preset-env": "^7.1.6", - "@babel/preset-flow": "^7.0.0", - "@babel/preset-typescript": "^7.1.0", - "@babel/register": "^7.0.0", - "babel-core": "^7.0.0-bridge.0", - "colors": "^1.1.2", - "flow-parser": "0.*", - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "neo-async": "^2.5.0", - "node-dir": "^0.1.17", - "recast": "^0.16.1", - "temp": "^0.8.1", - "write-file-atomic": "^2.3.0" - }, - "bin": { - "jscodeshift": "bin/jscodeshift.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5-writer": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/json5-writer/-/json5-writer-0.1.8.tgz", - "integrity": "sha512-h5sqkk/vSKvESOUTBniGWs8p8nTzHsoDrxPS9enJfQVINqXv3lm+FAyizLwbrCwCn0q7NXqDBb+r8AdUdK3XZw==", - "dev": true, - "dependencies": { - "jscodeshift": "^0.6.3" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/lastfm-node-client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/lastfm-node-client/-/lastfm-node-client-2.2.0.tgz", - "integrity": "sha512-nhHxrRPaNKIJnEuRov68HGjQRAFel2UWnG86ieQAklaVOdBciC6DO0vajZjzPZGqwvUAxQzDHPsp2ceDOtKW2w==", - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "node_modules/log-update": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-3.4.0.tgz", - "integrity": "sha512-ILKe88NeMt4gmDvk/eb615U/IVn7K9KWGkoYbdatQ69Z65nj1ZzjM6fHXfcs0Uge+e+EGnMW7DY4T9yko8vWFg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^3.2.0", - "cli-cursor": "^2.1.0", - "wrap-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/logform": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", - "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", - "dependencies": { - "@colors/colors": "1.5.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "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/logform/node_modules/safe-stable-stringify": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", - "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/node-dir": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", - "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.2" - }, - "engines": { - "node": ">= 0.10.5" - } - }, - "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", - "dev": true, - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", - "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "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/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/passport": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.3.tgz", - "integrity": "sha512-gGc+70h4gGdBWNsR3FuV3byLDY6KBTJAIExGFXTpQaYfbbcHCBlRRKx7RBQSpqEqc5Hh2qVzRs7ssvSfOpkUEA==", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-deezer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/passport-deezer/-/passport-deezer-0.2.0.tgz", - "integrity": "sha512-VNNkKc4ccpAsPFTu85mBkdtUGpipJbIohfC+ZUlDP8gkJKH/bPe7myz/Va+pKWSQ1LHsEHwo5Ljw1aARp6M+cA==", - "dependencies": { - "passport-oauth2": "^1.1.2" - } - }, - "node_modules/passport-oauth2": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz", - "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==", - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "node_modules/path-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.1.2.tgz", - "integrity": "sha512-p5kxPPwCdbf5AdXzT1bUBJomhgBlEjRBavYNr1XUpMFIE4Hnf2roueCMXudZK5tnaAu1tTmp3GPzqwJK45IHEA==", - "dev": true - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pony-cause": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", - "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-ms": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", - "dev": true, - "dependencies": { - "parse-ms": "^2.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "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/recast": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.16.2.tgz", - "integrity": "sha512-O/7qXi51DPjRVdbrpNzoBQH5dnAPQNbfoOFyRiUwreTMJfIHYOEBzwuH+c0+/BTSJ3CQyKs6ILSWXhESH6Op3A==", - "dev": true, - "dependencies": { - "ast-types": "0.11.7", - "esprima": "~4.0.0", - "private": "~0.1.5", - "source-map": "~0.6.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, - "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.0.tgz", - "integrity": "sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ==", - "dev": true, - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, - "node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "dev": true, - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "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/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", - "dev": true, - "dependencies": { - "ret": "~0.1.10" - } - }, - "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", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/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/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { - "is-extendable": "^0.1.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/set-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" + "wrappy": "1" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + "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": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" + "fn.name": "1.x.x" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/passport": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.3.tgz", + "integrity": "sha512-gGc+70h4gGdBWNsR3FuV3byLDY6KBTJAIExGFXTpQaYfbbcHCBlRRKx7RBQSpqEqc5Hh2qVzRs7ssvSfOpkUEA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "passport-strategy": "1.x.x", + "pause": "0.0.1" + }, + "engines": { + "node": ">= 0.4.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "node_modules/passport-deezer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/passport-deezer/-/passport-deezer-0.2.0.tgz", + "integrity": "sha512-VNNkKc4ccpAsPFTu85mBkdtUGpipJbIohfC+ZUlDP8gkJKH/bPe7myz/Va+pKWSQ1LHsEHwo5Ljw1aARp6M+cA==", "dependencies": { - "is-arrayish": "^0.3.1" + "passport-oauth2": "^1.1.2" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, + "node_modules/passport-oauth2": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz", + "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" }, "engines": { - "node": ">=10" + "node": ">= 0.4.0" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4.0" } }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "node_modules/path-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.1.2.tgz", + "integrity": "sha512-p5kxPPwCdbf5AdXzT1bUBJomhgBlEjRBavYNr1XUpMFIE4Hnf2roueCMXudZK5tnaAu1tTmp3GPzqwJK45IHEA==", + "dev": true + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/pony-cause": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", + "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" } }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { - "kind-of": "^3.2.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { - "is-buffer": "^1.1.5" + "side-channel": "^1.0.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "is-descriptor": "^0.1.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, + "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": { - "is-extendable": "^0.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, + "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/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", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dependencies": { - "kind-of": "^3.0.2" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { - "is-buffer": "^1.1.5" + "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" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, + "node_modules/send/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/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/snapdragon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "node_modules/snapdragon/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" } }, "node_modules/socket.io": { @@ -6879,58 +1792,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/spotify-web-api-node": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/spotify-web-api-node/-/spotify-web-api-node-5.0.2.tgz", @@ -6939,12 +1800,6 @@ "superagent": "^6.1.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -6953,102 +1808,6 @@ "node": "*" } }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", - "dev": true, - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -7100,18 +1859,6 @@ "node": ">=4" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/superagent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", @@ -7161,165 +1908,36 @@ }, "node_modules/superagent/node_modules/mime": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/superagent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/temp": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz", - "integrity": "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==", - "dev": true, - "dependencies": { - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "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", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0.0" } }, - "node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, + "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/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -7342,117 +1960,6 @@ "typescript": ">=4.1.0" } }, - "node_modules/ts-migrate": { - "version": "0.1.35", - "resolved": "https://registry.npmjs.org/ts-migrate/-/ts-migrate-0.1.35.tgz", - "integrity": "sha512-EmXahqwIP0a6fE1BLKKVMgJEeYn9V+CxJ19qFJ/DzkPL4PjDI/FcgPo8D519amBPy2nnlc/x1V6R6aIeHdD87w==", - "dev": true, - "dependencies": { - "create-jest-runner": "^0.5.3", - "json5": "^2.1.1", - "json5-writer": "^0.1.8", - "ts-migrate-plugins": "^0.1.35", - "ts-migrate-server": "^0.1.33", - "updatable-log": "^0.2.0", - "yargs": "^15.0.2" - }, - "bin": { - "ts-migrate": "build/cli.js", - "ts-migrate-full": "bin/ts-migrate-full.sh" - }, - "peerDependencies": { - "typescript": ">4.0" - } - }, - "node_modules/ts-migrate-plugins": { - "version": "0.1.35", - "resolved": "https://registry.npmjs.org/ts-migrate-plugins/-/ts-migrate-plugins-0.1.35.tgz", - "integrity": "sha512-DUkx7ClKhxKYPWDha9DJTZ6LhwEUszL90uH5I/O11K/6TbA96ytln1O3HL6Pt83i4mAKOlg0mQ6AMsPtL5FFkQ==", - "dev": true, - "dependencies": { - "eslint": "^7.14.0", - "jscodeshift": "^0.13.0", - "json-schema": "^0.4.0", - "ts-migrate-server": "^0.1.33" - }, - "peerDependencies": { - "typescript": ">4.0" - } - }, - "node_modules/ts-migrate-plugins/node_modules/ast-types": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", - "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", - "dev": true, - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ts-migrate-plugins/node_modules/jscodeshift": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.13.1.tgz", - "integrity": "sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.13.16", - "@babel/parser": "^7.13.16", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.12", - "@babel/plugin-transform-modules-commonjs": "^7.13.8", - "@babel/preset-flow": "^7.13.13", - "@babel/preset-typescript": "^7.13.0", - "@babel/register": "^7.13.16", - "babel-core": "^7.0.0-bridge.0", - "chalk": "^4.1.2", - "flow-parser": "0.*", - "graceful-fs": "^4.2.4", - "micromatch": "^3.1.10", - "neo-async": "^2.5.0", - "node-dir": "^0.1.17", - "recast": "^0.20.4", - "temp": "^0.8.4", - "write-file-atomic": "^2.3.0" - }, - "bin": { - "jscodeshift": "bin/jscodeshift.js" - }, - "peerDependencies": { - "@babel/preset-env": "^7.1.6" - } - }, - "node_modules/ts-migrate-plugins/node_modules/recast": { - "version": "0.20.5", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.20.5.tgz", - "integrity": "sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ==", - "dev": true, - "dependencies": { - "ast-types": "0.14.2", - "esprima": "~4.0.0", - "source-map": "~0.6.1", - "tslib": "^2.0.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ts-migrate-server": { - "version": "0.1.33", - "resolved": "https://registry.npmjs.org/ts-migrate-server/-/ts-migrate-server-0.1.33.tgz", - "integrity": "sha512-MYHy10yzL2fkb2FHFQ9f54gqc5KkaVthTjtpwS4bTroYCONDelp1hbz5nxKWaP3q2oc3kBVeGuAR91RNI+yK+g==", - "dev": true, - "dependencies": { - "@ts-morph/bootstrap": "^0.16.0", - "pretty-ms": "^7.0.1", - "updatable-log": "^0.2.0" - }, - "peerDependencies": { - "typescript": ">4.0" - } - }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -7532,36 +2039,6 @@ "json5": "lib/cli.js" } }, - "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -7607,347 +2084,127 @@ "typescript": "~4.6.0", "yargs": "^17.1.1" }, - "bin": { - "typescript-json-schema": "bin/typescript-json-schema" - } - }, - "node_modules/typescript-json-schema/node_modules/@types/node": { - "version": "16.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz", - "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", - "dev": true - }, - "node_modules/typescript-json-schema/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/typescript-json-schema/node_modules/safe-stable-stringify": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", - "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/typescript-json-schema/node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/typescript-json-schema/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/typescript-json-schema/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/typescript-json-schema/node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/typescript-json-schema/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "dependencies": { - "random-bytes": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uid2": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", - "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/union-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", - "dev": true, - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", - "dev": true, - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "bin": { + "typescript-json-schema": "bin/typescript-json-schema" } }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "node_modules/typescript-json-schema/node_modules/@types/node": { + "version": "16.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz", + "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", + "dev": true + }, + "node_modules/typescript-json-schema/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { - "isarray": "1.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "node_modules/typescript-json-schema/node_modules/safe-stable-stringify": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", "dev": true, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/updatable-log": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/updatable-log/-/updatable-log-0.2.0.tgz", - "integrity": "sha512-gR48/mTR6YFB+B1sNoap3nx8HFbEvDl0ej9KhlQTFZdmP8yL5fzFiCUfeHCUf1QvNnXowY1pM9iiGkPKrd0XyQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "figures": "^3.0.0", - "log-update": "^3.3.0" + "node": ">=10" } }, - "node_modules/updatable-log/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/typescript-json-schema/node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", "dev": true, - "dependencies": { - "color-convert": "^1.9.0" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4" + "node": ">=4.2.0" } }, - "node_modules/updatable-log/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/typescript-json-schema/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/updatable-log/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/typescript-json-schema/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "dependencies": { - "color-name": "1.1.3" + "engines": { + "node": ">=10" } }, - "node_modules/updatable-log/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/updatable-log/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/typescript-json-schema/node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, "engines": { - "node": ">=0.8.0" + "node": ">=12" } }, - "node_modules/updatable-log/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/typescript-json-schema/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/updatable-log/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", "dependencies": { - "has-flag": "^3.0.0" + "random-bytes": "~1.0.0" }, "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" } }, "node_modules/uri-js": { @@ -7958,22 +2215,6 @@ "punycode": "^2.1.0" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "dev": true - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7987,12 +2228,6 @@ "node": ">= 0.4.0" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -8007,27 +2242,6 @@ "node": ">= 0.8" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true - }, "node_modules/winston": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", @@ -8159,122 +2373,11 @@ "node": ">=10" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, "node_modules/ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", @@ -8295,98 +2398,11 @@ } } }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", -- 2.51.2 From dcde41f89acc8f7c8aa2d81c99aa0dc75b2f7a6b Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 14:48:27 -0500 Subject: [PATCH 24/64] Fix spotify source credential handling --- src/sources/SpotifySource.ts | 55 +++++++++++------------------------- 1 file changed, 16 insertions(+), 39 deletions(-) diff --git a/src/sources/SpotifySource.ts b/src/sources/SpotifySource.ts index 5b65e690..9b6034f1 100644 --- a/src/sources/SpotifySource.ts +++ b/src/sources/SpotifySource.ts @@ -34,8 +34,7 @@ export default class SpotifySource extends AbstractSource { this.logger.warn('Interval should be above 30 seconds...😬'); } - // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. - this.config.interval = interval; + this.config.data.interval = interval; this.workingCredsPath = `${this.configDir}/currentCreds-${name}.json`; this.canPoll = true; @@ -87,35 +86,26 @@ export default class SpotifySource extends AbstractSource { let spotifyCreds = {}; try { - spotifyCreds = await readJson(this.workingCredsPath, {throwOnNotFound: false}); + spotifyCreds = await readJson(this.workingCredsPath, {throwOnNotFound: false}) as any; } catch (e) { this.logger.warn('Current spotify credentials file exists but could not be parsed', { path: this.workingCredsPath }); } + const {token: accessToken = undefined, refreshToken = undefined} = (spotifyCreds || {}) as any; + const { - // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message - accessToken, - // @ts-expect-error TS(2339): Property 'clientId' does not exist on type '{}'. clientId, - // @ts-expect-error TS(2339): Property 'clientSecret' does not exist on type '{}... Remove this comment to see the full error message clientSecret, - // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{}'... Remove this comment to see the full error message redirectUri, - // @ts-expect-error TS(2339): Property 'refreshToken' does not exist on type '{}... Remove this comment to see the full error message - refreshToken, - } = this.config || {}; + } = this.config.data || {}; const rdUri = redirectUri || `${this.localUrl}/callback`; - - // @ts-expect-error TS(2339): Property 'token' does not exist on type '{}'. - const {token = accessToken, refreshToken: rt = refreshToken} = spotifyCreds || {}; - const apiConfig = { clientId, clientSecret, - accessToken: token, - refreshToken: rt, + accessToken, + refreshToken, } if (Object.values(apiConfig).every(x => x === undefined)) { @@ -127,25 +117,14 @@ export default class SpotifySource extends AbstractSource { const validationErrors = []; - if (token === undefined) { - if (clientId === undefined) { - validationErrors.push('clientId must be defined when access token is not present'); - } - if (clientSecret === undefined) { - validationErrors.push('clientSecret must be defined when access token is not present'); - } - if (rdUri === undefined) { - validationErrors.push('redirectUri must be defined when access token is not present'); - } - if (validationErrors.length !== 0) { - validationErrors.unshift('no access token is defined'); - } - } else if (rt === undefined && ( - clientId === undefined || - clientSecret === undefined || - rdUri === undefined - )) { - this.logger.warn('Access token is present but no refresh token is defined and remaining configuration is not sufficient to re-authorize. Without a refresh token API calls will fail after current token is expired.'); + if (clientId === undefined) { + validationErrors.push('clientId must be defined'); + } + if (clientSecret === undefined) { + validationErrors.push('clientSecret must be defined'); + } + if (rdUri === undefined) { + validationErrors.push('redirectUri must be defined'); } if (validationErrors.length !== 0) { @@ -215,11 +194,9 @@ export default class SpotifySource extends AbstractSource { callApi = async (func: any, retries = 0) => { const { - // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 1, - // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 2, - } = this.config; + } = this.config.data; try { return await func(this.spotifyApi); } catch (e) { -- 2.51.2 From 216d92cad7b2d858afc48724e14bf5b01cfa36f7 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 15:04:59 -0500 Subject: [PATCH 25/64] Add recentlyPlayed option interface --- src/sources/AbstractSource.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sources/AbstractSource.ts b/src/sources/AbstractSource.ts index 0c141f82..f29b32ee 100644 --- a/src/sources/AbstractSource.ts +++ b/src/sources/AbstractSource.ts @@ -4,6 +4,11 @@ import {InternalConfig, PlayObject, SourceType} from "../common/infrastructure/A import {Logger} from "winston"; import {SourceConfig} from "../common/infrastructure/config/source/sources.js"; +export interface RecentlyPlayedOptions { + limit?: number + formatted?: boolean +} + export default abstract class AbstractSource { name: string; @@ -52,7 +57,7 @@ export default abstract class AbstractSource { return this.authed; } - getRecentlyPlayed = async (options = {}): Promise => { + getRecentlyPlayed = async (options: RecentlyPlayedOptions = {}): Promise => { return []; } -- 2.51.2 From b1390cd74a8a91538bad3f1c9a9dbe0161950857 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 15:05:14 -0500 Subject: [PATCH 26/64] Update spotify client typings --- package-lock.json | 16 ++++++++++++++++ package.json | 1 + src/sources/SpotifySource.ts | 31 +++++++++++-------------------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1cac397..8a5ebdea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "@types/formidable": "^2.0.5", "@types/node": "^18.0.0", "@types/passport": "^1.0.7", + "@types/spotify-web-api-node": "^5.0.7", "@types/superagent": "^4.1.16", "@types/triple-beam": "^1.3.2", "ts-essentials": "^9.1.2", @@ -299,6 +300,21 @@ "@types/node": "*" } }, + "node_modules/@types/spotify-api": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/spotify-api/-/spotify-api-0.0.20.tgz", + "integrity": "sha512-HRBwiOthYAT0r/vUQlGlNTdXMnPg/CS3MzTuQKDy2PtalUDXhj0Ep9YZ4tXmL68K0j7FMcLFcYTzy7k7nVu5mg==", + "dev": true + }, + "node_modules/@types/spotify-web-api-node": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/spotify-web-api-node/-/spotify-web-api-node-5.0.7.tgz", + "integrity": "sha512-8ajd4xS3+l4Zau1OyggPv7DjeSFEIGYvG5Q8PbbBMKiaRFD53IkcvU4Bx4Ijyzw+l+Kc09L5L+MXRj0wyVLx9Q==", + "dev": true, + "dependencies": { + "@types/spotify-api": "*" + } + }, "node_modules/@types/superagent": { "version": "4.1.16", "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", diff --git a/package.json b/package.json index 232e9e11..dcb4ba88 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@types/formidable": "^2.0.5", "@types/node": "^18.0.0", "@types/passport": "^1.0.7", + "@types/spotify-web-api-node": "^5.0.7", "@types/superagent": "^4.1.16", "@types/triple-beam": "^1.3.2", "ts-essentials": "^9.1.2", diff --git a/src/sources/SpotifySource.ts b/src/sources/SpotifySource.ts index 9b6034f1..1cc16ba4 100644 --- a/src/sources/SpotifySource.ts +++ b/src/sources/SpotifySource.ts @@ -5,16 +5,17 @@ import { sortByPlayDate, sleep, parseRetryAfterSecsFromObj, } from "../utils.js"; import SpotifyWebApi from "spotify-web-api-node"; -import AbstractSource from "./AbstractSource.js"; +import AbstractSource, {RecentlyPlayedOptions} from "./AbstractSource.js"; import {SpotifySourceConfig} from "../common/infrastructure/config/source/spotify.js"; import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; +import PlayHistoryObject = SpotifyApi.PlayHistoryObject; const scopes = ['user-read-recently-played', 'user-read-currently-playing']; const state = 'random'; export default class SpotifySource extends AbstractSource { - spotifyApi: any; + spotifyApi: SpotifyWebApi; workingCredsPath: string; requiresAuth = true; @@ -40,22 +41,17 @@ export default class SpotifySource extends AbstractSource { this.canPoll = true; } - static formatPlayObj(obj: any, newFromSource = false): PlayObject { + static formatPlayObj(obj: PlayHistoryObject, newFromSource = false): PlayObject { const { track: { artists = [], - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message id, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message duration_ms, album: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name: albumName, } = {}, external_urls: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message spotify, } = {} } = {}, @@ -145,7 +141,7 @@ export default class SpotifySource extends AbstractSource { testAuth = async () => { try { - await this.callApi(((api: any) => api.getMe())); + await this.callApi>(((api: any) => api.getMe())); this.authed = true; } catch (e) { this.logger.error('Could not successfully communicate with Spotify API'); @@ -179,20 +175,16 @@ export default class SpotifySource extends AbstractSource { } } - getRecentlyPlayed = async (options = {}) => { - // @ts-expect-error TS(2339): Property 'limit' does not exist on type '{}'. - const {limit = 20, formatted = false} = options; - const func = (api: any) => api.getMyRecentlyPlayedTracks({ + getRecentlyPlayed = async (options: RecentlyPlayedOptions = {}) => { + const {limit = 20} = options; + const func = (api: SpotifyWebApi) => api.getMyRecentlyPlayedTracks({ limit }); - const result = await this.callApi(func); - if (formatted === true) { - return result.body.items.map((x: any) => SpotifySource.formatPlayObj(x)).sort(sortByPlayDate); - } - return result; + const result = await this.callApi>(func); + return result.body.items.map((x: any) => SpotifySource.formatPlayObj(x)).sort(sortByPlayDate); } - callApi = async (func: any, retries = 0) => { + callApi = async (func: (api: SpotifyWebApi) => Promise, retries = 0): Promise => { const { maxRequestRetries = 1, retryMultiplier = 2, @@ -209,7 +201,6 @@ export default class SpotifySource extends AbstractSource { const tokenResponse = await this.spotifyApi.refreshAccessToken(); const { body: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message access_token, // spotify may return a new refresh token // if it doesn't then continue to use the last refresh token we received -- 2.51.2 From 3d2296858fb8566bc71da3ee1e6e0de71d89ee78 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 15:15:24 -0500 Subject: [PATCH 27/64] Update subsonic source config usage --- package-lock.json | 125 ++++++++++++++++++++++++++++++++++ package.json | 1 + src/sources/SubsonicSource.ts | 22 +++--- 3 files changed, 135 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8a5ebdea..65568c5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "safe-stable-stringify": "^1.1.1", "socket.io": "^4.1.3", "spotify-web-api-node": "^5.0.2", + "subsonic-api": "^1.1.0", "superagent": "^6.1.0", "triple-beam": "^1.3.0", "winston": "^3.3.3", @@ -508,6 +509,14 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -667,6 +676,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -675,6 +692,14 @@ "node": ">=0.4.0" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/dayjs": { "version": "1.11.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", @@ -935,6 +960,28 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-stream-rotator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", @@ -1005,6 +1052,17 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/formidable": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", @@ -1179,6 +1237,11 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1277,6 +1340,16 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1369,6 +1442,41 @@ "node": ">= 0.6" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", + "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -1875,6 +1983,15 @@ "node": ">=4" } }, + "node_modules/subsonic-api": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/subsonic-api/-/subsonic-api-1.1.0.tgz", + "integrity": "sha512-LXZq6ft4JSwciUjaff8aapK8V250x4rI1hGZ1do0rUnkG8Li/8Qqtmy1NIxC5dWcoqCoa5pBjz8GEFFn4PSqWg==", + "dependencies": { + "md5": "^2.3.0", + "node-fetch": "^3.3.0" + } + }, "node_modules/superagent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", @@ -2258,6 +2375,14 @@ "node": ">= 0.8" } }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" + } + }, "node_modules/winston": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", diff --git a/package.json b/package.json index dcb4ba88..3ba718e1 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "safe-stable-stringify": "^1.1.1", "socket.io": "^4.1.3", "spotify-web-api-node": "^5.0.2", + "subsonic-api": "^1.1.0", "superagent": "^6.1.0", "triple-beam": "^1.3.0", "winston": "^3.3.3", diff --git a/src/sources/SubsonicSource.ts b/src/sources/SubsonicSource.ts index 7391427a..527516e9 100644 --- a/src/sources/SubsonicSource.ts +++ b/src/sources/SubsonicSource.ts @@ -6,6 +6,9 @@ import {buildTrackString, parseRetryAfterSecsFromObj, sleep} from "../utils.js"; import MemorySource from "./MemorySource.js"; import {SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic.js"; import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; +import {RecentlyPlayedOptions} from "./AbstractSource.js"; +import Subsonic from 'subsonic-api'; +// TODO replace homebrew api with subsonic-api dayjs.extend(isSameOrAfter); @@ -74,15 +77,12 @@ export class SubsonicSource extends MemorySource { callApi = async (req: any, retries = 0) => { const { - // @ts-expect-error TS(2339): Property 'user' does not exist on type '{}'. user, - // @ts-expect-error TS(2339): Property 'password' does not exist on type '{}'. password, - // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 1, - // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5 - } = this.config; + } = this.config.data; + const salt = await crypto.randomBytes(10).toString('hex'); const hash = crypto.createHash('md5').update(`${password}${salt}`).digest('hex') @@ -150,8 +150,7 @@ export class SubsonicSource extends MemorySource { } initialize = async () => { - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url} = this.config; + const {url} = this.config.data; try { await request.get(`${url}/`); this.logger.info('Subsonic Connection: ok'); @@ -168,8 +167,7 @@ export class SubsonicSource extends MemorySource { } testAuth= async () => { - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url} = this.config; + const {url} = this.config.data; try { await this.callApi(request.get(`${url}/rest/ping`)); this.authed = true; @@ -181,11 +179,9 @@ export class SubsonicSource extends MemorySource { return this.authed; } - getRecentlyPlayed = async (options = {}) => { - // @ts-expect-error TS(2339): Property 'formatted' does not exist on type '{}'. + getRecentlyPlayed = async (options: RecentlyPlayedOptions = {}) => { const {formatted = false} = options; - // @ts-expect-error TS(2339): Property 'url' does not exist on type '{}'. - const {url} = this.config; + const {url} = this.config.data; const resp = await this.callApi(request.get(`${url}/rest/getNowPlaying`)); const { nowPlaying: { -- 2.51.2 From fdf00d8acc909869222ba366be24e71c67aac5a0 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 15:18:09 -0500 Subject: [PATCH 28/64] Update deezer config usage --- src/sources/DeezerSource.ts | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/src/sources/DeezerSource.ts b/src/sources/DeezerSource.ts index cfb14339..0622774b 100644 --- a/src/sources/DeezerSource.ts +++ b/src/sources/DeezerSource.ts @@ -1,7 +1,7 @@ import request from 'superagent'; import {parseRetryAfterSecsFromObj, readJson, sleep, sortByPlayDate, writeFile} from "../utils.js"; import {Strategy as DeezerStrategy} from 'passport-deezer'; -import AbstractSource from "./AbstractSource.js"; +import AbstractSource, {RecentlyPlayedOptions} from "./AbstractSource.js"; import dayjs from "dayjs"; import {DeezerSourceConfig} from "../common/infrastructure/config/source/deezer.js"; import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; @@ -75,18 +75,14 @@ export default class DeezerSource extends AbstractSource { initialize = async () => { try { const credFile = await readJson(this.workingCredsPath, {throwOnNotFound: false}); - // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message - this.config.accessToken = credFile.accessToken; + this.config.data.accessToken = credFile.accessToken; } catch (e) { this.logger.warn('Current deezer credentials file exists but could not be parsed', { path: this.workingCredsPath }); } - // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message - if(this.config.accessToken === undefined) { - // @ts-expect-error TS(2339): Property 'clientId' does not exist on type '{}'. - if(this.config.clientId === undefined) { + if(this.config.data.accessToken === undefined) { + if(this.config.data.clientId === undefined) { throw new Error('clientId must be defined when accessToken is not present'); - // @ts-expect-error TS(2339): Property 'clientSecret' does not exist on type '{}... Remove this comment to see the full error message - } else if(this.config.clientSecret === undefined) { + } else if(this.config.data.clientSecret === undefined) { throw new Error('clientSecret must be defined when accessToken is not present'); } } @@ -105,8 +101,7 @@ export default class DeezerSource extends AbstractSource { return this.authed; } - getRecentlyPlayed = async (options = {}) => { - // @ts-expect-error TS(2339): Property 'formatted' does not exist on type '{}'. + getRecentlyPlayed = async (options: RecentlyPlayedOptions = {}) => { const {formatted = false} = options; const resp = await this.callApi(request.get(`${this.baseUrl}/user/me/history`)); if(formatted) { @@ -117,15 +112,12 @@ export default class DeezerSource extends AbstractSource { callApi = async (req: any, retries = 0) => { const { - // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 1, - // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5 - } = this.config; + } = this.config.data; req.query({ - // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message - access_token: this.config.accessToken, + access_token: this.config.data.accessToken, output: 'json' }); try { @@ -188,12 +180,9 @@ export default class DeezerSource extends AbstractSource { generatePassportStrategy = () => { return new DeezerStrategy({ - // @ts-expect-error TS(2339): Property 'clientId' does not exist on type '{}'. - clientID: this.config.clientId, - // @ts-expect-error TS(2339): Property 'clientSecret' does not exist on type '{}... Remove this comment to see the full error message - clientSecret: this.config.clientSecret, - // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{}'... Remove this comment to see the full error message - callbackURL: this.config.redirectUri || `${this.localUrl}/deezer/callback`, + clientID: this.config.data.clientId, + clientSecret: this.config.data.clientSecret, + callbackURL: this.config.data.redirectUri || `${this.localUrl}/deezer/callback`, scope: ['listening_history','offline_access'], }, (accessToken: any, refreshToken: any, profile: any, done: any) => { // return done(null, { @@ -222,8 +211,7 @@ export default class DeezerSource extends AbstractSource { id, displayName, })); - // @ts-expect-error TS(2339): Property 'accessToken' does not exist on type '{}'... Remove this comment to see the full error message - this.config.accessToken = accessToken; + this.config.data.accessToken = accessToken; this.logger.info('Got token Deezer SDK callback!'); return true; } else { -- 2.51.2 From 04b07d6bfbfcffbb8c0e6131a03315a49a43ea87 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 15:18:31 -0500 Subject: [PATCH 29/64] Update abstract source config usage --- src/sources/AbstractSource.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sources/AbstractSource.ts b/src/sources/AbstractSource.ts index f29b32ee..b9713570 100644 --- a/src/sources/AbstractSource.ts +++ b/src/sources/AbstractSource.ts @@ -199,8 +199,7 @@ export default abstract class AbstractSource { this.tracksDiscovered += scrobbleResult.length; } - // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. - const {interval = 30, checkActiveFor = 300, maxSleep = 300} = this.config; + const {interval = 30, checkActiveFor = 300, maxSleep = 300} = this.config.data; let sleepTime = interval; // don't need to do back off calc if interval is 5 minutes or greater since its already pretty light on API calls -- 2.51.2 From de3832f3ceaf4055e2108fd2556e5a818eea7240 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 9 Feb 2023 16:09:52 -0500 Subject: [PATCH 30/64] More TS fixes --- src/apis/LastfmApiClient.ts | 5 +- src/clients/AbstractScrobbleClient.ts | 42 +++++------ src/clients/LastfmScrobbler.ts | 30 +++----- src/clients/MalojaScrobbler.ts | 59 +++++++-------- src/clients/ScrobbleClients.ts | 11 +-- src/common/infrastructure/Atomic.ts | 70 ++++++++++++++++++ .../infrastructure/config/client/lastfm.ts | 3 +- src/index.ts | 71 +++++++------------ src/sources/DeezerSource.ts | 3 +- src/sources/JellyfinSource.ts | 4 +- src/sources/LastfmSource.ts | 7 +- src/sources/PlexSource.ts | 4 +- src/sources/ScrobbleSources.ts | 2 +- src/utils.ts | 52 +++----------- 14 files changed, 173 insertions(+), 190 deletions(-) diff --git a/src/apis/LastfmApiClient.ts b/src/apis/LastfmApiClient.ts index c128998b..3dd7ad59 100644 --- a/src/apis/LastfmApiClient.ts +++ b/src/apis/LastfmApiClient.ts @@ -22,6 +22,7 @@ const retryErrors = [ export default class LastfmApiClient extends AbstractApiClient { user?: string; + declare config: LastfmData; constructor(name: any, config: Partial, options = {}) { super('lastfm', name, config, options); @@ -79,9 +80,7 @@ export default class LastfmApiClient extends AbstractApiClient { callApi = async (func: any, retries = 0): Promise => { const { - // @ts-expect-error TS(2339): Property 'maxRequestRetries' does not exist on typ... Remove this comment to see the full error message maxRequestRetries = 2, - // @ts-expect-error TS(2339): Property 'retryMultiplier' does not exist on type ... Remove this comment to see the full error message retryMultiplier = 1.5 } = this.config; @@ -110,9 +109,7 @@ export default class LastfmApiClient extends AbstractApiClient { } getAuthUrl = () => { - // @ts-expect-error TS(2339): Property 'redirectUri' does not exist on type '{}'... Remove this comment to see the full error message const redir = `${this.config.redirectUri}?state=${this.name}`; - // @ts-expect-error TS(2339): Property 'apiKey' does not exist on type '{}'. return `http://www.last.fm/api/auth/?api_key=${this.config.apiKey}&cb=${encodeURIComponent(redir)}` } diff --git a/src/clients/AbstractScrobbleClient.ts b/src/clients/AbstractScrobbleClient.ts index bdce4d8f..3ec9a935 100644 --- a/src/clients/AbstractScrobbleClient.ts +++ b/src/clients/AbstractScrobbleClient.ts @@ -1,6 +1,13 @@ import dayjs, {Dayjs} from "dayjs"; import {buildTrackString, capitalize, createLabelledLogger, playObjDataMatch} from "../utils.js"; -import {ClientType, INITIALIZED, INITIALIZING, InitState, NOT_INITIALIZED} from "../common/infrastructure/Atomic.js"; +import { + ClientType, + INITIALIZED, + INITIALIZING, + InitState, + NOT_INITIALIZED, + PlayObject, ScrobbledPlayObject +} from "../common/infrastructure/Atomic.js"; import {Logger} from "winston"; import {CommonClientConfig} from "../common/infrastructure/config/client/index.js"; import {ClientConfig} from "../common/infrastructure/config/client/clients.js"; @@ -16,8 +23,8 @@ export default abstract class AbstractScrobbleClient { requiresAuthInteraction: boolean = false; authed: boolean = false; - recentScrobbles = []; - scrobbledPlayObjs = []; + recentScrobbles: PlayObject[] = []; + scrobbledPlayObjs: ScrobbledPlayObject[] = []; newestScrobbleTime?: Dayjs oldestScrobbleTime: Dayjs = dayjs(); tracksScrobbled: number = 0; @@ -37,15 +44,15 @@ export default abstract class AbstractScrobbleClient { this.logger = createLabelledLogger(identifier, identifier); const { - // @ts-expect-error TS(2339): Property 'options' does not exist on type '{}'. - options: { - refreshEnabled = true, - checkExistingScrobbles = true, - verbose = {}, - } = {}, - ...rest + data: { + options: { + refreshEnabled = true, + checkExistingScrobbles = true, + verbose = {}, + } = {}, + }, } = config; - this.config = rest; + this.config = config; this.refreshEnabled = refreshEnabled; this.checkExistingScrobbles = checkExistingScrobbles; @@ -109,7 +116,7 @@ export default abstract class AbstractScrobbleClient { this.logger.debug('Scrobbler does not have refresh function implemented!'); } - alreadyScrobbled = async () => { + alreadyScrobbled = async (playObj: PlayObject, log = false) => { this.logger.debug('Scrobbler does not have alreadyScrobbled check implemented!'); return false; } @@ -143,10 +150,9 @@ export default abstract class AbstractScrobbleClient { this.scrobbledPlayObjs.push({play: playObj, scrobble: this.formatPlayObj(scrobbleResp)}); } - cleanSourceSearchTitle = (playObj: any) => { + cleanSourceSearchTitle = (playObj: PlayObject) => { const { data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {}, } = playObj; @@ -154,14 +160,12 @@ export default abstract class AbstractScrobbleClient { return track; }; - findExistingSubmittedPlayObj = (playObj: any) => { + findExistingSubmittedPlayObj = (playObj: PlayObject): ([undefined, undefined] | [ScrobbledPlayObject, ScrobbledPlayObject[]]) => { const { data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, } = {} } = playObj; @@ -172,15 +176,13 @@ export default abstract class AbstractScrobbleClient { return [undefined, undefined]; } - const matchPlayDate = dtInvariantMatches.find((x) => { + const matchPlayDate = dtInvariantMatches.find((x: ScrobbledPlayObject) => { const { play: { data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate: sPlayDate } = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source: playSource } = {}, } = {}, diff --git a/src/clients/LastfmScrobbler.ts b/src/clients/LastfmScrobbler.ts index fa2facc7..f94cbf89 100644 --- a/src/clients/LastfmScrobbler.ts +++ b/src/clients/LastfmScrobbler.ts @@ -9,7 +9,7 @@ import { truncateStringToLength, } from "../utils.js"; import LastfmApiClient from "../apis/LastfmApiClient.js"; -import {INITIALIZING} from "../common/infrastructure/Atomic.js"; +import {INITIALIZING, PlayObject, TrackStringOptions} from "../common/infrastructure/Atomic.js"; import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm.js"; import {TrackScrobbleResponse, UserGetRecentTracksResponse} from "lastfm-node-client"; @@ -22,8 +22,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { declare config: LastfmClientConfig; constructor(name: any, config: LastfmClientConfig, options = {}) { - // @ts-expect-error TS(2554): Expected 2-3 arguments, but got 4. - super('lastfm', name, config, options); + super('lastfm', name, config); this.api = new LastfmApiClient(name, config.data, options) } @@ -99,24 +98,22 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { this.lastScrobbleCheck = dayjs(); } - cleanSourceSearchTitle = (playObj: any) => { + cleanSourceSearchTitle = (playObj: PlayObject) => { const { data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {}, } = playObj; return track.toLocaleLowerCase().trim(); } - // @ts-expect-error TS(2416): Property 'alreadyScrobbled' in type 'LastfmScrobbl... Remove this comment to see the full error message - alreadyScrobbled = async (playObj: any, log = false) => { + alreadyScrobbled = async (playObj: PlayObject, log = false) => { return this.existingScrobble(playObj, (log || this.verboseOptions.match.onMatch)) !== undefined; } - existingScrobble = (playObj: any, logMatch = false) => { + existingScrobble = (playObj: PlayObject, logMatch = false) => { const tr = truncateStringToLength(27); - const scoreTrackOpts = {include: ['track', 'time'], transformers: {track: (t: any) => tr(t).padEnd(30)}}; + const scoreTrackOpts: TrackStringOptions = {include: ['track', 'time'], transformers: {track: (t: any) => tr(t).padEnd(30)}}; // return early if we don't care about checking existing if (false === this.checkExistingScrobbles) { @@ -127,7 +124,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { } let existingScrobble; - let closestMatch = {score: 0, breakdowns: ['None']}; + let closestMatch: {score: number, breakdowns: string[], scrobble?: PlayObject} = {score: 0, breakdowns: ['None']}; // then check if we have already recorded this const [existingExactSubmitted, existingDataSubmitted = []] = this.findExistingSubmittedPlayObj(playObj); @@ -162,13 +159,10 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { const { data: { artists: sourceArtists = [], - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message trackLength, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, } = {}, } = playObj; @@ -180,7 +174,6 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { const referenceMatch = referenceApiScrobbleResponse !== undefined && playObjDataMatch(x, referenceApiScrobbleResponse); - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message const {data: {playDate: scrobbleTime, track: scrobbleTitle, artists = []} = {}} = x; const playDiffThreshold = source === 'Subsonic' ? 60 : 10; @@ -246,30 +239,23 @@ export default class LastfmScrobbler extends AbstractScrobbleClient { } if ((existingScrobble !== undefined && this.verboseOptions.match.onMatch) || (existingScrobble === undefined && this.verboseOptions.match.onNoMatch)) { - // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type '{ scor... Remove this comment to see the full error message const closestScrobble = closestMatch.scrobble === undefined ? closestMatch.breakdowns.join(' | ') : `Closest Scrobble: ${buildTrackString(closestMatch.scrobble, scoreTrackOpts)} => ${closestMatch.breakdowns.join(' | ')}`; this.logger.debug(`(Existing Check) Source: ${buildTrackString(playObj, scoreTrackOpts)} => ${closestScrobble}`); } return existingScrobble; } - scrobble = async (playObj: any) => { + scrobble = async (playObj: PlayObject) => { const { data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message duration, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, data = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, newFromSource = false, } = {} diff --git a/src/clients/MalojaScrobbler.ts b/src/clients/MalojaScrobbler.ts index d5cdf4d8..a55b0f80 100644 --- a/src/clients/MalojaScrobbler.ts +++ b/src/clients/MalojaScrobbler.ts @@ -11,7 +11,17 @@ import { truncateStringToLength, parseRetryAfterSecsFromObj } from "../utils.js"; -import {INITIALIZING} from "../common/infrastructure/Atomic.js"; +import { + INITIALIZING, + MalojaScrobbleData, + MalojaScrobbleRequestData, + MalojaScrobbleV2RequestData, + MalojaScrobbleV3RequestData, + MalojaV2ScrobbleData, + MalojaV3ScrobbleData, + PlayObject, + TrackStringOptions +} from "../common/infrastructure/Atomic.js"; import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja.js"; const feat = ["ft.", "ft", "feat.", "feat", "featuring", "Ft.", "Ft", "Feat.", "Feat", "Featuring"]; @@ -35,7 +45,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } } - static formatPlayObj(obj: any, serverVersion = undefined) { + static formatPlayObj(obj: MalojaScrobbleData, serverVersion = undefined): PlayObject { let artists, title, album, @@ -48,23 +58,18 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { // when the track was scrobbled time: mTime, track: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists: mArtists, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message title: mTitle, album: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message name: mAlbum, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists: albumArtists } = {}, // length of the track - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message length: mLength, } = {}, // how long the track was listened to before it was scrobbled duration: mDuration, - } = obj; + } = obj as MalojaV3ScrobbleData; artists = mArtists; time = mTime; title = mTitle; @@ -78,7 +83,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { album: mAlbum, duration: mDuration, time: mTime, - } = obj; + } = obj as MalojaV2ScrobbleData; artists = mArtists; title = mTitle; album = mAlbum; @@ -97,7 +102,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { }, []); return { data: { - artists: [...new Set(artistStrings)], + artists: [...new Set(artistStrings)] as string[], track: title, album, duration, @@ -186,8 +191,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { const {url} = this.config.data; try { - // @ts-expect-error TS(2345): Argument of type '{ maxRequestRetries: number; }' ... Remove this comment to see the full error message - const serverInfoResp = await this.callApi(request.get(`${url}/apis/mlj_1/serverinfo`), {maxRequestRetries: 0}); + const serverInfoResp = await this.callApi(request.get(`${url}/apis/mlj_1/serverinfo`), 0); const { statusCode, body: { @@ -319,10 +323,9 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { this.lastScrobbleCheck = dayjs(); } - cleanSourceSearchTitle = (playObj: any) => { + cleanSourceSearchTitle = (playObj: PlayObject) => { const { data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, artists: sourceArtists = [], } = {}, @@ -343,14 +346,13 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { return lowerTitle; } - // @ts-expect-error TS(2416): Property 'alreadyScrobbled' in type 'MalojaScrobbl... Remove this comment to see the full error message alreadyScrobbled = async (playObj: any, log = false) => { return this.existingScrobble(playObj, (log || this.verboseOptions.match.onMatch)) !== undefined; } - existingScrobble = (playObj: any, logMatch = false) => { + existingScrobble = (playObj: PlayObject, logMatch = false) => { const tr = truncateStringToLength(27); - const scoreTrackOpts = {include: ['track', 'time'], transformers: {track: (t: any) => tr(t).padEnd(30)}}; + const scoreTrackOpts: TrackStringOptions = {include: ['track', 'time'], transformers: {track: (t: any) => tr(t).padEnd(30)}}; // return early if we don't care about checking existing if (false === this.checkExistingScrobbles) { @@ -361,7 +363,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } let existingScrobble; - let closestMatch = {score: 0, breakdowns: ['None']}; + let closestMatch: {score: number, breakdowns: string[], scrobble?: PlayObject} = {score: 0, breakdowns: ['None']}; // then check if we have already recorded this const [existingExactSubmitted, existingDataSubmitted = []] = this.findExistingSubmittedPlayObj(playObj); @@ -396,13 +398,10 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { const { data: { artists: sourceArtists = [], - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message trackLength, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, } = {}, } = playObj; @@ -414,7 +413,6 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { const referenceMatch = referenceApiScrobbleResponse !== undefined && playObjDataMatch(x, referenceApiScrobbleResponse); - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message const {data: {playDate: scrobbleTime, track: scrobbleTitle, artists = []} = {}} = x; const playDiffThreshold = source === 'Subsonic' ? 60 : 10; @@ -480,31 +478,24 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { } if ((existingScrobble !== undefined && this.verboseOptions.match.onMatch) || (existingScrobble === undefined && this.verboseOptions.match.onNoMatch)) { - // @ts-expect-error TS(2339): Property 'scrobble' does not exist on type '{ scor... Remove this comment to see the full error message const closestScrobble = closestMatch.scrobble === undefined ? closestMatch.breakdowns.join(' | ') : `Closest Scrobble: ${buildTrackString(closestMatch.scrobble, scoreTrackOpts)} => ${closestMatch.breakdowns.join(' | ')}`; this.logger.debug(`(Existing Check) Source: ${buildTrackString(playObj, scoreTrackOpts)} => ${closestScrobble}`); } return existingScrobble; } - scrobble = async (playObj: any) => { + scrobble = async (playObj: PlayObject) => { const {url, apiKey} = this.config.data; const { data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message duration, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source, newFromSource = false, } = {} @@ -512,7 +503,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { const sType = newFromSource ? 'New' : 'Backlog'; - const scrobbleData = { + const scrobbleData: MalojaScrobbleRequestData = { title: track, album, key: apiKey, @@ -524,13 +515,11 @@ export default class MalojaScrobbler extends AbstractScrobbleClient { try { // 3.0.3 has a BC for something (maybe seconds => length ?) -- see #42 in repo if(this.serverVersion === undefined || compareVersions(this.serverVersion, '3.0.2') > 0) { - // @ts-expect-error TS(2339): Property 'artists' does not exist on type '{ title... Remove this comment to see the full error message - scrobbleData.artists = artists; + (scrobbleData as MalojaScrobbleV3RequestData).artists = artists; } else { // maloja seems to detect this deliminator much better than commas // also less likely artist has a forward slash in their name than a comma - // @ts-expect-error TS(2339): Property 'artist' does not exist on type '{ title:... Remove this comment to see the full error message - scrobbleData.artist = artists.join(' / '); + (scrobbleData as MalojaScrobbleV2RequestData).artist = artists.join(' / '); } const response = await this.callApi(request.post(`${url}/apis/mlj_1/newscrobble`) diff --git a/src/clients/ScrobbleClients.ts b/src/clients/ScrobbleClients.ts index 193e3748..f9841dce 100644 --- a/src/clients/ScrobbleClients.ts +++ b/src/clients/ScrobbleClients.ts @@ -1,15 +1,14 @@ -import dayjs from "dayjs"; +import dayjs, {Dayjs} from "dayjs"; import { createAjvFactory, createLabelledLogger, - isValidConfigStructure, playObjDataMatch, readJson, returnDuplicateStrings, validateJson } from "../utils.js"; import MalojaScrobbler from "./MalojaScrobbler.js"; import LastfmScrobbler from "./LastfmScrobbler.js"; -import {clientTypes, ConfigMeta} from "../common/infrastructure/Atomic.js"; +import {clientTypes, ConfigMeta, PlayObject} from "../common/infrastructure/Atomic.js"; import {AIOConfig} from "../common/infrastructure/config/aioConfig.js"; import * as aioSchema from '../common/schema/aio.json' assert {type: "json"}; import * as clientSchema from '../common/schema/client.json' assert {type: "json"}; @@ -280,16 +279,12 @@ ${sources.join('\n')}`); * @param {{scrobbleFrom, scrobbleTo, forceRefresh: boolean}|{scrobbleFrom, scrobbleTo}} options * @returns {Array} */ - scrobble = async (data: any, options = {}) => { + scrobble = async (data: (PlayObject | PlayObject[]), options: {forceRefresh?: boolean, checkTime?: Dayjs, scrobbleTo?: string[], scrobbleFrom?: string} = {}) => { const playObjs = Array.isArray(data) ? data : [data]; const { - // @ts-expect-error TS(2339): Property 'forceRefresh' does not exist on type '{}... Remove this comment to see the full error message forceRefresh = false, - // @ts-expect-error TS(2339): Property 'checkTime' does not exist on type '{}'. checkTime = dayjs(), - // @ts-expect-error TS(2339): Property 'scrobbleTo' does not exist on type '{}'. scrobbleTo = [], - // @ts-expect-error TS(2339): Property 'scrobbleFrom' does not exist on type '{}... Remove this comment to see the full error message scrobbleFrom = 'source', } = options; diff --git a/src/common/infrastructure/Atomic.ts b/src/common/infrastructure/Atomic.ts index 9e514ee6..5ef03042 100644 --- a/src/common/infrastructure/Atomic.ts +++ b/src/common/infrastructure/Atomic.ts @@ -58,3 +58,73 @@ export interface PlayObject { data: PlayData, meta: PlayMeta } + +export interface TrackStringOptions { + include?: ('time' | 'artist' | 'track' | 'timeFromNow' | 'sourceId')[] + transformers?: { + artists?: (a: string[]) => string + track?: (t: string) => string + time?: (t: Dayjs) => string + timeFromNow?: (t: Dayjs) => string + } +} + +export interface ScrobbledPlayObject { + play: PlayObject + scrobble: PlayObject +} + +export interface MalojaV2ScrobbleData { + artists: string[] + title: string + album: string + /** + * Length of the track + * */ + duration: number + /** + * unix timestamp (seconds) scrobble was made at + * */ + time: number +} + +export interface MalojaV3ScrobbleData { + /** + * unix timestamp (seconds) scrobble was made at + * */ + time: number + track: { + artists: string[] + title: string + album?: { + name: string + artists: string[] + } + /** + * length of the track + * */ + length: number + } + /** + * how long the track was listened to before it was scrobbled + * */ + duration: number +} + +export type MalojaScrobbleData = MalojaV2ScrobbleData | MalojaV3ScrobbleData; + +export interface MalojaScrobbleRequestData { + key: string + title: string + album: string + time: number + length: number +} + +export interface MalojaScrobbleV2RequestData extends MalojaScrobbleRequestData { + artist: string +} + +export interface MalojaScrobbleV3RequestData extends MalojaScrobbleRequestData { + artists: string[] +} diff --git a/src/common/infrastructure/config/client/lastfm.ts b/src/common/infrastructure/config/client/lastfm.ts index f9aea172..b583a6df 100644 --- a/src/common/infrastructure/config/client/lastfm.ts +++ b/src/common/infrastructure/config/client/lastfm.ts @@ -1,6 +1,7 @@ import {CommonClientConfig, CommonClientData} from "./index.js"; +import {RequestRetryOptions} from "../common.js"; -export interface LastfmData { +export interface LastfmData extends RequestRetryOptions { /** * API Key generated from Last.fm account * */ diff --git a/src/index.ts b/src/index.ts index eff850ae..80cb8abd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,10 @@ import LastfmApiClient from "./apis/LastfmApiClient.js"; import LastfmSource from "./sources/LastfmSource.js"; import LastfmScrobbler from "./clients/LastfmScrobbler.js"; import ScrobbleClients from "./clients/ScrobbleClients.js"; +import DeezerSource from "./sources/DeezerSource.js"; +import AbstractSource from "./sources/AbstractSource.js"; +import {PlayObject, TrackStringOptions} from "./common/infrastructure/Atomic.js"; +import SpotifySource from "./sources/SpotifySource.js"; dayjs.extend(utc) @@ -102,11 +106,11 @@ const myTransports = [ ]; if (typeof logPath === 'string') { - // @ts-expect-error TS(2345): Argument of type 'DailyRotateFile' is not assignab... Remove this comment to see the full error message + // @ts-ignore myTransports.push(rotateTransport); } -const loggerOptions = { +const loggerOptions: winston.LoggerOptions = { level: logConfig.level, format: labelledFormat(), transports: myTransports, @@ -154,9 +158,8 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) } // initialize deezer strategies - const deezerSources = scrobbleSources.getByType('deezer'); + const deezerSources = scrobbleSources.getByType('deezer') as DeezerSource[]; for(const d of deezerSources) { - // @ts-expect-error TS(2339): Property 'name' does not exist on type 'never'. passport.use(`deezer-${d.name}`, d.generatePassportStrategy()); } @@ -179,6 +182,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) authed = false, } = x; const base = { + status: '', type, display: capitalize(type), tracksDiscovered, @@ -189,16 +193,12 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) authed, }; if(!initialized) { - // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = 'Not Initialized'; } else if(requiresAuth && !authed) { - // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = requiresAuthInteraction ? 'Auth Interaction Required' : 'Authentication Failed Or Not Attempted' } else if(canPoll) { - // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = polling ? 'Running' : 'Idle'; } else { - // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = tracksDiscovered > 0 ? 'Received Data' : 'Awaiting Data' } return base; @@ -214,6 +214,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) authed = false, } = x; const base = { + status: '', type, display: capitalize(type), tracksDiscovered: tracksScrobbled, @@ -221,13 +222,10 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) hasAuth: requiresAuth, }; if(!initialized) { - // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = 'Not Initialized'; } else if(requiresAuth && !authed) { - // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = requiresAuthInteraction ? 'Auth Interaction Required' : 'Authentication Failed Or Not Attempted' } else { - // @ts-expect-error TS(2339): Property 'status' does not exist on type '{ type: ... Remove this comment to see the full error message base.status = tracksScrobbled > 0 ? 'Received Data' : 'Awaiting Data'; } return base; @@ -276,8 +274,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const plexMiddle = plexRequestMiddle(); const plexLog = createLabelledLogger('plexReq', 'Plex Request'); app.postAsync('/plex', plexMiddle, async function (req, res) { - // @ts-expect-error TS(2339): Property 'payload' does not exist on type 'Request... Remove this comment to see the full error message - const { payload } = req; + const { payload } = req as any; if(payload === undefined) { plexLog.warn('Received a request without any data'); @@ -285,13 +282,12 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) } else { const playObj = PlexSource.formatPlayObj(payload, true); - const pSources = scrobbleSources.getByType('plex'); + const pSources = scrobbleSources.getByType('plex') as PlexSource[]; if(pSources.length === 0) { plexLog.warn('Received valid Plex webhook payload but no Plex sources are configured'); } for (const source of pSources) { - // @ts-expect-error TS(2339): Property 'handle' does not exist on type 'never'. await source.handle(playObj, scrobbleClients); } } @@ -303,9 +299,8 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const jellyfinJsonParser = bodyParser.json({type: 'text/*'}); app.postAsync('/jellyfin', jellyfinJsonParser, async function (req, res) { const playObj = JellyfinSource.formatPlayObj(req.body, true); - const pSources = scrobbleSources.getByType('jellyfin'); + const pSources = scrobbleSources.getByType('jellyfin') as JellyfinSource[]; for (const source of pSources) { - // @ts-expect-error TS(2339): Property 'handle' does not exist on type 'never'. await source.handle(playObj, scrobbleClients); } res.send('OK'); @@ -314,9 +309,8 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) app.use('/client/auth', clientCheckMiddle); app.getAsync('/client/auth', async function (req, res) { const { - // @ts-expect-error TS(2339): Property 'scrobbleClient' does not exist on type '... Remove this comment to see the full error message scrobbleClient, - } = req; + } = req as any; switch (scrobbleClient.type) { case 'lastfm': @@ -382,19 +376,18 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) return res.status(400).send(`Specified source cannot retrieve recent plays (${source.type})`); } - const result = await source.getRecentlyPlayed({formatted: true}); + const result = await (source as AbstractSource).getRecentlyPlayed({formatted: true}); const artistTruncFunc = truncateStringToLength(Math.min(40, longestString(result.map((x: any) => x.data.artists.join(' / ')).flat()))); const trackLength = longestString(result.map((x: any) => x.data.track)) - const plays = result.map((x: any) => { + const plays = result.map((x: PlayObject) => { const { meta: { url: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message web } = {} } = {} } = x; - const buildOpts = { + const buildOpts: TrackStringOptions = { include: ['time', 'timeFromNow', 'track', 'artist'], transformers: { artists: (a: any) => artistTruncFunc(a.join(' / ')).padEnd(33), @@ -414,19 +407,15 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) for (const [setting, val] of Object.entries(req.query)) { switch (setting) { case 'limit': - // @ts-expect-error TS(2345): Argument of type 'string | ParsedQs | string[] | P... Remove this comment to see the full error message - logConfig.limit = Number.parseInt(val); + logConfig.limit = Number.parseInt(val as string); break; case 'sort': - // @ts-expect-error TS(2322): Type 'string | ParsedQs | string[] | ParsedQs[] | ... Remove this comment to see the full error message - logConfig.sort = val; + logConfig.sort = val as string; break; case 'level': - // @ts-expect-error TS(2322): Type 'string | ParsedQs | string[] | ParsedQs[] | ... Remove this comment to see the full error message - logConfig.level = val; + logConfig.level = val as string; for (const [key, logger] of winston.loggers.loggers) { - // @ts-expect-error TS(2322): Type 'string | ParsedQs | string[] | ParsedQs[] | ... Remove this comment to see the full error message - logger.level = val; + logger.level = val as string; } break; } @@ -443,18 +432,16 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) // so to get around this add an additional middleware that loops/sleeps until we should have fetched everything ¯\_(ツ)_/¯ app.getAsync(/.*deezer\/callback*$/, function (req, res, next) { // @ts-expect-error TS(2339): Property 'deezerSource' does not exist on type 'Se... Remove this comment to see the full error message - const entity = scrobbleSources.getByName(req.session.deezerSource); + const entity = scrobbleSources.getByName(req.session.deezerSource as string); const passportFunc = passport.authenticate(`deezer-${entity.name}`, {session: false}); return passportFunc(req, res, next); }, async function (req, res) { // @ts-expect-error TS(2339): Property 'deezerSource' does not exist on type 'Se... Remove this comment to see the full error message - let entity = scrobbleSources.getByName(req.session.deezerSource); + let entity = scrobbleSources.getByName(req.session.deezerSource as string) as DeezerSource; for(let i = 0; i < 3; i++) { - // @ts-expect-error TS(2532): Object is possibly 'undefined'. if(entity.error !== undefined) { return res.send('Error with deezer credentials storage'); - // @ts-expect-error TS(2532): Object is possibly 'undefined'. - } else if(entity.config.accessToken !== undefined) { + } else if(entity.config.data.accessToken !== undefined) { // start polling entity.poll(entity.clients) return res.redirect('/'); @@ -493,9 +480,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) // but eventually should update all source callbacks to url specific URLS to avoid ambiguity... // wish we could use state param to identify name/source but not all auth strategies and auth provides may provide access to that logger.info('Received auth code callback from Spotify', {label: 'Spotify'}); - // @ts-expect-error TS(2554): Expected 1 arguments, but got 2. - const source = scrobbleSources.getByName(state, 'spotify'); - // @ts-expect-error TS(2532): Object is possibly 'undefined'. + const source = scrobbleSources.getByNameAndType(state as string, 'spotify') as SpotifySource; const tokenResult = await source.handleAuthCodeCallback(req.query); let responseContent = 'OK'; if (tokenResult === true) { @@ -520,13 +505,11 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) await sleep(1500); // stagger polling by 1.5 seconds so that log messages for each source don't get mixed up switch (source.type) { case 'spotify': - // @ts-expect-error TS(2339): Property 'spotifyApi' does not exist on type 'neve... Remove this comment to see the full error message - if (source.spotifyApi !== undefined) { - // @ts-expect-error TS(2339): Property 'spotifyApi' does not exist on type 'neve... Remove this comment to see the full error message - if (source.spotifyApi.getAccessToken() === undefined) { + if ((source as SpotifySource).spotifyApi !== undefined) { + if ((source as SpotifySource).spotifyApi.getAccessToken() === undefined) { anyNotReady = true; } else { - source.poll(scrobbleClients); + (source as SpotifySource).poll(scrobbleClients); } } break; diff --git a/src/sources/DeezerSource.ts b/src/sources/DeezerSource.ts index 0622774b..c38a64bc 100644 --- a/src/sources/DeezerSource.ts +++ b/src/sources/DeezerSource.ts @@ -29,8 +29,7 @@ export default class DeezerSource extends AbstractSource { this.logger.warn('Interval should be above 30 seconds...😬'); } - // @ts-expect-error TS(2339): Property 'interval' does not exist on type '{}'. - this.config.interval = interval; + this.config.data.interval = interval; this.workingCredsPath = `${this.configDir}/currentCreds-${name}.json`; this.canPoll = true; diff --git a/src/sources/JellyfinSource.ts b/src/sources/JellyfinSource.ts index 6d786b03..c8e51ded 100644 --- a/src/sources/JellyfinSource.ts +++ b/src/sources/JellyfinSource.ts @@ -89,15 +89,13 @@ export default class JellyfinSource extends MemorySource { } } - isValidEvent = (playObj: any) => { + isValidEvent = (playObj: PlayObject) => { const { meta: { mediaType, event, user, server }, data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {} } = playObj; diff --git a/src/sources/LastfmSource.ts b/src/sources/LastfmSource.ts index f35e4258..ff228628 100644 --- a/src/sources/LastfmSource.ts +++ b/src/sources/LastfmSource.ts @@ -1,4 +1,4 @@ -import AbstractSource from "./AbstractSource.js"; +import AbstractSource, {RecentlyPlayedOptions} from "./AbstractSource.js"; import LastfmApiClient from "../apis/LastfmApiClient.js"; import {sortByPlayDate} from "../utils.js"; import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm.js"; @@ -40,9 +40,8 @@ export default class LastfmSource extends AbstractSource { } - getRecentlyPlayed = async(options = {}) => { - // @ts-expect-error TS(2339): Property 'limit' does not exist on type '{}'. - const {limit = 20, formatted = false} = options; + getRecentlyPlayed = async(options: RecentlyPlayedOptions = {}) => { + const {limit = 20} = options; const resp = await this.api.callApi((client: any) => client.userGetRecentTracks({user: this.api.user, limit, extended: true})); const { recenttracks: { diff --git a/src/sources/PlexSource.ts b/src/sources/PlexSource.ts index 4d5dcb59..48b692b4 100644 --- a/src/sources/PlexSource.ts +++ b/src/sources/PlexSource.ts @@ -101,15 +101,13 @@ export default class PlexSource extends AbstractSource { } } - isValidEvent = (playObj: any) => { + isValidEvent = (playObj: PlayObject) => { const { meta: { mediaType, event, user, library, server }, data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, } = {} } = playObj; diff --git a/src/sources/ScrobbleSources.ts b/src/sources/ScrobbleSources.ts index 15f554f6..f742a85f 100644 --- a/src/sources/ScrobbleSources.ts +++ b/src/sources/ScrobbleSources.ts @@ -1,4 +1,4 @@ -import {createLabelledLogger, isValidConfigStructure, readJson, validateJson} from "../utils.js"; +import {createLabelledLogger, readJson, validateJson} from "../utils.js"; import SpotifySource from "./SpotifySource.js"; import PlexSource from "./PlexSource.js"; import TautulliSource from "./TautulliSource.js"; diff --git a/src/utils.ts b/src/utils.ts index f2841ad6..a3037427 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,11 +1,12 @@ import {promises, constants} from "fs"; -import dayjs from 'dayjs'; +import dayjs, {Dayjs} from 'dayjs'; import utc from 'dayjs/plugin/utc.js'; import winston, {Logger} from "winston"; import stringify from 'safe-stable-stringify'; import { TimeoutError, WebapiError } from "spotify-web-api-node/src/response-error.js"; import { Response } from 'superagent'; import Ajv, {Schema} from 'ajv'; +import {PlayObject, TrackStringOptions} from "./common/infrastructure/Atomic.js"; const {format} = winston; const {combine, printf, timestamp, label, splat, errors} = format; @@ -16,8 +17,7 @@ export async function readJson(this: any, path: any, {logErrors = true, throwOnN try { await promises.access(path, constants.R_OK); const data = await promises.readFile(path); - // @ts-expect-error TS(2345): Argument of type 'Buffer' is not assignable to par... Remove this comment to see the full error message - return JSON.parse(data); + return JSON.parse(data as unknown as string); } catch (e) { const {code} = e; if (code === 'ENOENT') { @@ -80,31 +80,24 @@ export const truncateStringToLength = (length: any, truncStr = '...') => (str: a const defaultTransformer = (input: any) => input; -export const buildTrackString = (playObj: any, options = {}) => { +export const buildTrackString = (playObj: PlayObject, options: TrackStringOptions = {}) => { const { - // @ts-expect-error TS(2339): Property 'include' does not exist on type '{}'. include = ['time', 'artist', 'track'], - // @ts-expect-error TS(2339): Property 'transformers' does not exist on type '{}... Remove this comment to see the full error message transformers: { - artists: artistsFunc = (a: any) => a.join(' / '), + artists: artistsFunc = (a: string[]) => a.join(' / '), track: trackFunc = defaultTransformer, - time: timeFunc = (t: any) => t.local().format(), - timeFromNow = (t: any) => t.local().fromNow(), + time: timeFunc = (t: Dayjs) => t.local().format(), + timeFromNow = (t: Dayjs) => t.local().fromNow(), } = {} } = options; const { data: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message artists, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message playDate } = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message sourceId } = {}, } = playObj; @@ -199,25 +192,6 @@ export const setIntersection = (setA: any, setB: any) => { return _intersection } -export const isValidConfigStructure = (obj: any, required = {}) => { - // @ts-expect-error TS(2339): Property 'name' does not exist on type '{}'. - const {name = false, type = false, data = true} = required; - const errs = []; - if (obj.type === undefined && type) { - errs.push("'type' must be defined"); - } - if (obj.name === undefined && name) { - errs.push("'name' must be defined") - } - if (obj.data === undefined && data) { - errs.push("'data' must be defined"); - } - if (errs.length > 0) { - return errs; - } - return true; -} - export const returnDuplicateStrings = (arr: any) => { const alreadySeen: any = []; const dupes: any = []; @@ -235,19 +209,15 @@ export const capitalize = (str: any) => { * Checks sources and source ID's (unique identifiers) first then * Checks track, album, and artists in that order * */ -export const playObjDataMatch = (a: any, b: any) => { +export const playObjDataMatch = (a: PlayObject, b: PlayObject) => { const { data: { artists: aArtists = [], - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album: aAlbum, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track: aTrack, } = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source: aSource, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message sourceId: aSourceId, } = {}, } = a; @@ -255,15 +225,11 @@ export const playObjDataMatch = (a: any, b: any) => { const { data: { artists: bArtists = [], - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message album: bAlbum, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message track: bTrack, } = {}, meta: { - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message source: bSource, - // @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message sourceId: bSourceId, } = {}, } = b; @@ -332,7 +298,7 @@ export const parseRetryAfterSecsFromObj = (err: any) => { return retryAfter; // got a number! } // try to parse as date - // @ts-expect-error TS(2322): Type 'Dayjs' is not assignable to type 'number'. + // @ts-ignore retryAfter = dayjs(retryAfter); if (!dayjs.isDayjs(retryAfter)) { return undefined; // could not parse string if not in ISO 8601 format -- 2.51.2 From 22bbcfe3e5abe589ed40e3b82730e5844f615556 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 10:18:05 -0500 Subject: [PATCH 31/64] Remove unused subsonic library --- package-lock.json | 125 ---------------------------------- package.json | 1 - src/sources/SubsonicSource.ts | 2 - 3 files changed, 128 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65568c5a..8a5ebdea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,6 @@ "safe-stable-stringify": "^1.1.1", "socket.io": "^4.1.3", "spotify-web-api-node": "^5.0.2", - "subsonic-api": "^1.1.0", "superagent": "^6.1.0", "triple-beam": "^1.3.0", "winston": "^3.3.3", @@ -509,14 +508,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "engines": { - "node": "*" - } - }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -676,14 +667,6 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "engines": { - "node": "*" - } - }, "node_modules/cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -692,14 +675,6 @@ "node": ">=0.4.0" } }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, "node_modules/dayjs": { "version": "1.11.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", @@ -960,28 +935,6 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, "node_modules/file-stream-rotator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", @@ -1052,17 +1005,6 @@ "node": ">= 6" } }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/formidable": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", @@ -1237,11 +1179,6 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1340,16 +1277,6 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1442,41 +1369,6 @@ "node": ">= 0.6" } }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", - "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, "node_modules/oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -1983,15 +1875,6 @@ "node": ">=4" } }, - "node_modules/subsonic-api": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/subsonic-api/-/subsonic-api-1.1.0.tgz", - "integrity": "sha512-LXZq6ft4JSwciUjaff8aapK8V250x4rI1hGZ1do0rUnkG8Li/8Qqtmy1NIxC5dWcoqCoa5pBjz8GEFFn4PSqWg==", - "dependencies": { - "md5": "^2.3.0", - "node-fetch": "^3.3.0" - } - }, "node_modules/superagent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", @@ -2375,14 +2258,6 @@ "node": ">= 0.8" } }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, "node_modules/winston": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", diff --git a/package.json b/package.json index 3ba718e1..dcb4ba88 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "safe-stable-stringify": "^1.1.1", "socket.io": "^4.1.3", "spotify-web-api-node": "^5.0.2", - "subsonic-api": "^1.1.0", "superagent": "^6.1.0", "triple-beam": "^1.3.0", "winston": "^3.3.3", diff --git a/src/sources/SubsonicSource.ts b/src/sources/SubsonicSource.ts index 527516e9..3e25dae6 100644 --- a/src/sources/SubsonicSource.ts +++ b/src/sources/SubsonicSource.ts @@ -7,8 +7,6 @@ import MemorySource from "./MemorySource.js"; import {SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic.js"; import {InternalConfig, PlayObject} from "../common/infrastructure/Atomic.js"; import {RecentlyPlayedOptions} from "./AbstractSource.js"; -import Subsonic from 'subsonic-api'; -// TODO replace homebrew api with subsonic-api dayjs.extend(isSameOrAfter); -- 2.51.2 From 317da71ec3f8d346f53457e17f8218a750135dad Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 10:18:15 -0500 Subject: [PATCH 32/64] Ignore .bak files --- .gitignore | 2 ++ config/config.json.bak | 31 ------------------------------- 2 files changed, 2 insertions(+), 31 deletions(-) delete mode 100644 config/config.json.bak diff --git a/.gitignore b/.gitignore index b7868577..70c02601 100644 --- a/.gitignore +++ b/.gitignore @@ -126,3 +126,5 @@ dist src/**/**.js src/**/**.js.map + +*.bak diff --git a/config/config.json.bak b/config/config.json.bak deleted file mode 100644 index 54ab4c78..00000000 --- a/config/config.json.bak +++ /dev/null @@ -1,31 +0,0 @@ -{ - "sourceDefaults": { - "maxPollRetries": 0, - "maxRequestRetries": 1, - "retryMultiplier": 1.5 - }, - "clientDefaults": { - "maxRequestRetries": 1, - "retryMultiplier": 1.5 - }, - "sources": [ - { - "type": "tautulli", - "name": "myTautulli", - "clients": ["myLFM"], - "data": { - "user": ["myName"] - } - } - ], - "clients": [ - { - "type": "lastfm", - "name": "myLFM", - "data": { - "apiKey": "xxxxxxxxxxx", - "secret": "xxxxxxxxxxx" - } - } - ] -} -- 2.51.2 From fbbcfc5a57ac9dc5067033b67b3cbe6173787899 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 11:09:43 -0500 Subject: [PATCH 33/64] Downgrade socket.io to avoid ESM module issue --- package-lock.json | 319 ++++++++++++++++++++++++---------------------- package.json | 4 +- 2 files changed, 166 insertions(+), 157 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8a5ebdea..295eaca8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "passport-deezer": "^0.2.0", "pony-cause": "^1.1.1", "safe-stable-stringify": "^1.1.1", - "socket.io": "^4.1.3", + "socket.io": "4.1.3", "spotify-web-api-node": "^5.0.2", "superagent": "^6.1.0", "triple-beam": "^1.3.0", @@ -50,7 +50,7 @@ "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", "typescript": "^4.9.5", - "typescript-json-schema": "~0.53" + "typescript-json-schema": "~0.55" }, "engines": { "node": ">=18.0.0", @@ -88,16 +88,6 @@ "node": ">=12" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@dabh/diagnostics": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", @@ -123,10 +113,15 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } }, "node_modules/@tsconfig/node10": { "version": "1.0.9", @@ -168,6 +163,11 @@ "@types/node": "*" } }, + "node_modules/@types/component-emitter": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" + }, "node_modules/@types/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-2.0.0.tgz", @@ -342,6 +342,18 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -420,6 +432,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -508,6 +528,20 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -770,31 +804,31 @@ } }, "node_modules/engine.io": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.0.tgz", - "integrity": "sha512-OgxY1c/RuCSeO/rTr8DIFXx76IzUUft86R7/P7MMbbkuzeqJoTNw2lmeD91IyGz41QYleIIjWeMJGgug043sfQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.1.1.tgz", + "integrity": "sha512-aMWot7H5aC8L4/T8qMYbLdvKlZOdJTH54FxfdFunTGvhMx1BHkJOntWArsVfgAZVwAO9LC2sryPWRcEeUzCe5w==", "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" + "engine.io-parser": "~4.0.0", + "ws": "~7.4.2" }, "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.3.tgz", + "integrity": "sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==", + "dependencies": { + "base64-arraybuffer": "0.1.4" + }, "engines": { - "node": ">=10.0.0" + "node": ">=8.0.0" } }, "node_modules/engine.io/node_modules/cookie": { @@ -1221,6 +1255,18 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -1340,9 +1386,9 @@ } }, "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1493,9 +1539,9 @@ } }, "node_modules/path-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.1.2.tgz", - "integrity": "sha512-p5kxPPwCdbf5AdXzT1bUBJomhgBlEjRBavYNr1XUpMFIE4Hnf2roueCMXudZK5tnaAu1tTmp3GPzqwJK45IHEA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.2.5.tgz", + "integrity": "sha512-i73IctDr3F2W+bsOWDyyVm/lqsXO47aY9nsFZUjTT/aljSbkxHxxCoyZ9UUrM8jK0JVod+An+rl48RCsvWM+9g==", "dev": true }, "node_modules/path-is-absolute": { @@ -1731,35 +1777,36 @@ } }, "node_modules/socket.io": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.0.tgz", - "integrity": "sha512-b65bp6INPk/BMMrIgVvX12x3Q+NqlGqSlTuvKQWt0BUJ3Hyy3JangBl7fEoWZTXbOKlCqNPbQ6MbWgok/km28w==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.3.tgz", + "integrity": "sha512-tLkaY13RcO4nIRh1K2hT5iuotfTaIQw7cVIe0FUykN3SuQi0cm7ALxuyT5/CtDswOMWUzMGTibxYNx/gU7In+Q==", "dependencies": { + "@types/cookie": "^0.4.0", + "@types/cors": "^2.8.10", + "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.4.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" + "debug": "~4.3.1", + "engine.io": "~5.1.1", + "socket.io-adapter": "~2.3.1", + "socket.io-parser": "~4.0.4" }, "engines": { "node": ">=10.0.0" } }, "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "dependencies": { - "ws": "~8.11.0" - } + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", + "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" }, "node_modules/socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.5.tgz", + "integrity": "sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig==", "dependencies": { - "@socket.io/component-emitter": "~3.1.0", + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", "debug": "~4.3.1" }, "engines": { @@ -2019,18 +2066,6 @@ } } }, - "node_modules/ts-node/node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -2043,18 +2078,6 @@ "strip-bom": "^3.0.0" } }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2086,18 +2109,18 @@ } }, "node_modules/typescript-json-schema": { - "version": "0.53.1", - "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.53.1.tgz", - "integrity": "sha512-Hg+RnOKUd38MOzC0rDft03a8xvwO+gCcj1F77smw2tCoZYQpFoLtrXWBGdvCX+REliko5WYel2kux17HPFqjLQ==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.55.0.tgz", + "integrity": "sha512-BXaivYecUdiXWWNiUqXgY6A9cMWerwmhtO+lQE7tDZGs7Mf38sORDeQZugfYOZOHPZ9ulsD+w0LWjFDOQoXcwg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/node": "^16.9.2", "glob": "^7.1.7", - "path-equal": "1.1.2", + "path-equal": "^1.1.2", "safe-stable-stringify": "^2.2.0", - "ts-node": "^10.2.1", - "typescript": "~4.6.0", + "ts-node": "^10.9.1", + "typescript": "~4.8.2", "yargs": "^17.1.1" }, "bin": { @@ -2110,20 +2133,6 @@ "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", "dev": true }, - "node_modules/typescript-json-schema/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/typescript-json-schema/node_modules/safe-stable-stringify": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", @@ -2134,9 +2143,9 @@ } }, "node_modules/typescript-json-schema/node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2146,59 +2155,6 @@ "node": ">=4.2.0" } }, - "node_modules/typescript-json-schema/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/typescript-json-schema/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/typescript-json-schema/node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/typescript-json-schema/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -2389,17 +2345,34 @@ "node": ">=10" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", "engines": { - "node": ">=10.0.0" + "node": ">=8.3.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -2414,11 +2387,47 @@ } } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index dcb4ba88..10494a15 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "passport-deezer": "^0.2.0", "pony-cause": "^1.1.1", "safe-stable-stringify": "^1.1.1", - "socket.io": "^4.1.3", + "socket.io": "4.1.3", "spotify-web-api-node": "^5.0.2", "superagent": "^6.1.0", "triple-beam": "^1.3.0", @@ -75,6 +75,6 @@ "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", "typescript": "^4.9.5", - "typescript-json-schema": "~0.53" + "typescript-json-schema": "~0.55" } } -- 2.51.2 From 0583a59177d33b867c804eae224dc0a401474f52 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 12:08:37 -0500 Subject: [PATCH 34/64] Regenerate json schema files --- src/common/schema/aio.json | 444 ++++++++++++++++++---------------- src/common/schema/client.json | 58 +++-- src/common/schema/source.json | 132 +++++----- 3 files changed, 335 insertions(+), 299 deletions(-) diff --git a/src/common/schema/aio.json b/src/common/schema/aio.json index d08608c5..0c5ff1f1 100644 --- a/src/common/schema/aio.json +++ b/src/common/schema/aio.json @@ -1,7 +1,20 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "CommonClientData": { + "Record": { + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/clients\",{assert:{\"resolution-mode\":\"import\"}}).ClientAIOConfig": { + "anyOf": [ + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmClientAIOConfig" + }, + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/maloja\",{assert:{\"resolution-mode\":\"import\"}}).MalojaClientAIOConfig" + } + ] + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData": { "properties": { "maxRequestRetries": { "default": 1, @@ -19,7 +32,7 @@ "verbose": { "properties": { "match": { - "$ref": "#/definitions/MatchLoggingOptions" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).MatchLoggingOptions" } }, "type": "object" @@ -35,30 +48,153 @@ }, "type": "object" }, - "CommonSourceData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).MatchLoggingOptions": { "properties": { - "maxPollRetries": { - "default": 0, - "description": "default # of automatic polling restarts on error", - "type": "number" + "confidenceBreakdown": { + "type": "boolean" + }, + "onMatch": { + "type": "boolean" + }, + "onNoMatch": { + "type": "boolean" + } + }, + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmClientAIOConfig": { + "properties": { + "configureAs": { + "enum": [ + "client", + "source" + ], + "type": "string" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData" + }, + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData" + } + ] + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + }, + "type": { + "enum": [ + "lastfm" + ], + "type": "string" + } + }, + "required": [ + "data", + "name", + "type" + ], + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", "type": "number" }, - "options": { - "$ref": "#/definitions/Record" + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", "type": "number" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" } }, + "required": [ + "apiKey", + "secret" + ], "type": "object" }, - "DeezerData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/maloja\",{assert:{\"resolution-mode\":\"import\"}}).MalojaClientAIOConfig": { + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData" + }, + { + "properties": { + "apiKey": { + "description": "API Key for Maloja server", + "type": "string" + }, + "url": { + "description": "URL for maloja server", + "type": "string" + } + }, + "required": [ + "apiKey", + "url" + ], + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + }, + "type": { + "enum": [ + "maloja" + ], + "type": "string" + } + }, + "required": [ + "data", + "name", + "type" + ], + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/common\",{assert:{\"resolution-mode\":\"import\"}}).RequestRetryOptions": { + "properties": { + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerData": { "properties": { "clientId": { "description": "deezer client id", @@ -104,7 +240,7 @@ ], "type": "object" }, - "DeezerSourceAIOConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerSourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -114,7 +250,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/DeezerData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerData" }, "name": { "description": "Unique identifier for this source.", @@ -133,7 +269,50 @@ ], "type": "object" }, - "JellySourceAIOConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).SourceRetryOptions": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/jellyfin\",{assert:{\"resolution-mode\":\"import\"}}).JellySourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -145,7 +324,7 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonSourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" }, { "properties": { @@ -179,7 +358,7 @@ ], "type": "object" }, - "LastFmSouceAIOConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastFmSouceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -197,10 +376,10 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonSourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" }, { - "$ref": "#/definitions/LastfmData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData" } ] }, @@ -221,128 +400,7 @@ ], "type": "object" }, - "LastfmClientAIOConfig": { - "properties": { - "configureAs": { - "enum": [ - "client", - "source" - ], - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonClientData" - }, - { - "$ref": "#/definitions/LastfmData" - } - ] - }, - "name": { - "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", - "type": "string" - }, - "type": { - "enum": [ - "lastfm" - ], - "type": "string" - } - }, - "required": [ - "data", - "name", - "type" - ], - "type": "object" - }, - "LastfmData": { - "properties": { - "apiKey": { - "description": "API Key generated from Last.fm account", - "type": "string" - }, - "redirectUri": { - "default": "http://localhost:9078/lastfm/callback", - "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", - "type": "string" - }, - "secret": { - "description": "Secret generated from Last.fm account", - "type": "string" - }, - "session": { - "description": "Optional session id returned from a completed auth flow", - "type": "string" - } - }, - "required": [ - "apiKey", - "secret" - ], - "type": "object" - }, - "MalojaClientAIOConfig": { - "properties": { - "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonClientData" - }, - { - "properties": { - "apiKey": { - "description": "API Key for Maloja server", - "type": "string" - }, - "url": { - "description": "URL for maloja server", - "type": "string" - } - }, - "required": [ - "apiKey", - "url" - ], - "type": "object" - } - ] - }, - "name": { - "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", - "type": "string" - }, - "type": { - "enum": [ - "maloja" - ], - "type": "string" - } - }, - "required": [ - "data", - "name", - "type" - ], - "type": "object" - }, - "MatchLoggingOptions": { - "properties": { - "confidenceBreakdown": { - "type": "boolean" - }, - "onMatch": { - "type": "boolean" - }, - "onNoMatch": { - "type": "boolean" - } - }, - "type": "object" - }, - "PlexSourceAIOConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/plex\",{assert:{\"resolution-mode\":\"import\"}}).PlexSourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -354,7 +412,7 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonSourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" }, { "properties": { @@ -392,45 +450,32 @@ ], "type": "object" }, - "Record": { - "type": "object" - }, - "RequestRetryOptions": { - "properties": { - "maxRequestRetries": { - "default": 1, - "description": "default # of http request retries a source can make before error is thrown", - "type": "number" + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/sources\",{assert:{\"resolution-mode\":\"import\"}}).SourceAIOConfig": { + "anyOf": [ + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceAIOConfig" }, - "retryMultiplier": { - "default": 1.5, - "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", - "type": "number" - } - }, - "type": "object" - }, - "SourceRetryOptions": { - "properties": { - "maxPollRetries": { - "default": 0, - "description": "default # of automatic polling restarts on error", - "type": "number" + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/plex\",{assert:{\"resolution-mode\":\"import\"}}).PlexSourceAIOConfig" }, - "maxRequestRetries": { - "default": 1, - "description": "default # of http request retries a source can make before error is thrown", - "type": "number" + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/tautulli\",{assert:{\"resolution-mode\":\"import\"}}).TautulliSourceAIOConfig" }, - "retryMultiplier": { - "default": 1.5, - "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", - "type": "number" + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerSourceAIOConfig" + }, + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicSourceAIOConfig" + }, + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/jellyfin\",{assert:{\"resolution-mode\":\"import\"}}).JellySourceAIOConfig" + }, + { + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastFmSouceAIOConfig" } - }, - "type": "object" + ] }, - "SpotifySourceAIOConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -440,7 +485,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/SpotifySourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceData" }, "name": { "description": "Unique identifier for this source.", @@ -459,7 +504,7 @@ ], "type": "object" }, - "SpotifySourceData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceData": { "properties": { "clientId": { "description": "spotify client id", @@ -505,7 +550,7 @@ ], "type": "object" }, - "SubsonicData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicData": { "properties": { "maxPollRetries": { "default": 0, @@ -543,7 +588,7 @@ ], "type": "object" }, - "SubsonicSourceAIOConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicSourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -553,7 +598,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/SubsonicData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicData" }, "name": { "description": "Unique identifier for this source.", @@ -572,7 +617,7 @@ ], "type": "object" }, - "TautulliSourceAIOConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/tautulli\",{assert:{\"resolution-mode\":\"import\"}}).TautulliSourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -584,7 +629,7 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonSourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" }, { "properties": { @@ -625,49 +670,20 @@ }, "properties": { "clientDefaults": { - "$ref": "#/definitions/RequestRetryOptions" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/common\",{assert:{\"resolution-mode\":\"import\"}}).RequestRetryOptions" }, "clients": { "items": { - "anyOf": [ - { - "$ref": "#/definitions/MalojaClientAIOConfig" - }, - { - "$ref": "#/definitions/LastfmClientAIOConfig" - } - ] + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/clients\",{assert:{\"resolution-mode\":\"import\"}}).ClientAIOConfig" }, "type": "array" }, "sourceDefaults": { - "$ref": "#/definitions/SourceRetryOptions" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).SourceRetryOptions" }, "sources": { "items": { - "anyOf": [ - { - "$ref": "#/definitions/SpotifySourceAIOConfig" - }, - { - "$ref": "#/definitions/PlexSourceAIOConfig" - }, - { - "$ref": "#/definitions/TautulliSourceAIOConfig" - }, - { - "$ref": "#/definitions/DeezerSourceAIOConfig" - }, - { - "$ref": "#/definitions/SubsonicSourceAIOConfig" - }, - { - "$ref": "#/definitions/JellySourceAIOConfig" - }, - { - "$ref": "#/definitions/LastFmSouceAIOConfig" - } - ] + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/sources\",{assert:{\"resolution-mode\":\"import\"}}).SourceAIOConfig" }, "type": "array" } diff --git a/src/common/schema/client.json b/src/common/schema/client.json index 972e08ef..a4c35aaf 100644 --- a/src/common/schema/client.json +++ b/src/common/schema/client.json @@ -2,14 +2,14 @@ "$schema": "http://json-schema.org/draft-07/schema#", "anyOf": [ { - "$ref": "#/definitions/MalojaClientConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmClientConfig" }, { - "$ref": "#/definitions/LastfmClientConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/maloja\",{assert:{\"resolution-mode\":\"import\"}}).MalojaClientConfig" } ], "definitions": { - "CommonClientData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData": { "properties": { "maxRequestRetries": { "default": 1, @@ -27,7 +27,7 @@ "verbose": { "properties": { "match": { - "$ref": "#/definitions/MatchLoggingOptions" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).MatchLoggingOptions" } }, "type": "object" @@ -43,7 +43,21 @@ }, "type": "object" }, - "LastfmClientConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).MatchLoggingOptions": { + "properties": { + "confidenceBreakdown": { + "type": "boolean" + }, + "onMatch": { + "type": "boolean" + }, + "onNoMatch": { + "type": "boolean" + } + }, + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmClientConfig": { "properties": { "configureAs": { "enum": [ @@ -55,10 +69,10 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonClientData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData" }, { - "$ref": "#/definitions/LastfmData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData" } ] }, @@ -73,17 +87,27 @@ ], "type": "object" }, - "LastfmData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData": { "properties": { "apiKey": { "description": "API Key generated from Last.fm account", "type": "string" }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, "redirectUri": { "default": "http://localhost:9078/lastfm/callback", "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", "type": "string" }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, "secret": { "description": "Secret generated from Last.fm account", "type": "string" @@ -99,12 +123,12 @@ ], "type": "object" }, - "MalojaClientConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/maloja\",{assert:{\"resolution-mode\":\"import\"}}).MalojaClientConfig": { "properties": { "data": { "allOf": [ { - "$ref": "#/definitions/CommonClientData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData" }, { "properties": { @@ -135,20 +159,6 @@ "name" ], "type": "object" - }, - "MatchLoggingOptions": { - "properties": { - "confidenceBreakdown": { - "type": "boolean" - }, - "onMatch": { - "type": "boolean" - }, - "onNoMatch": { - "type": "boolean" - } - }, - "type": "object" } } } diff --git a/src/common/schema/source.json b/src/common/schema/source.json index 2686c544..d83ace20 100644 --- a/src/common/schema/source.json +++ b/src/common/schema/source.json @@ -2,52 +2,68 @@ "$schema": "http://json-schema.org/draft-07/schema#", "anyOf": [ { - "$ref": "#/definitions/SpotifySourceConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceConfig" }, { - "$ref": "#/definitions/PlexSourceConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/plex\",{assert:{\"resolution-mode\":\"import\"}}).PlexSourceConfig" }, { - "$ref": "#/definitions/TautulliSourceConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/tautulli\",{assert:{\"resolution-mode\":\"import\"}}).TautulliSourceConfig" }, { - "$ref": "#/definitions/DeezerSourceConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerSourceConfig" }, { - "$ref": "#/definitions/SubSonicSourceConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubSonicSourceConfig" }, { - "$ref": "#/definitions/JellySourceConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/jellyfin\",{assert:{\"resolution-mode\":\"import\"}}).JellySourceConfig" }, { - "$ref": "#/definitions/LastfmSourceConfig" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmSourceConfig" } ], "definitions": { - "CommonSourceData": { + "Record": { + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData": { "properties": { - "maxPollRetries": { - "default": 0, - "description": "default # of automatic polling restarts on error", - "type": "number" + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", "type": "number" }, - "options": { - "$ref": "#/definitions/Record" + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", "type": "number" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" } }, + "required": [ + "apiKey", + "secret" + ], "type": "object" }, - "DeezerData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerData": { "properties": { "clientId": { "description": "deezer client id", @@ -93,7 +109,7 @@ ], "type": "object" }, - "DeezerSourceConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -103,7 +119,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/DeezerData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerData" }, "name": { "description": "Unique identifier for this source.", @@ -115,7 +131,30 @@ ], "type": "object" }, - "JellySourceConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/jellyfin\",{assert:{\"resolution-mode\":\"import\"}}).JellySourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -127,7 +166,7 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonSourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" }, { "properties": { @@ -154,33 +193,7 @@ ], "type": "object" }, - "LastfmData": { - "properties": { - "apiKey": { - "description": "API Key generated from Last.fm account", - "type": "string" - }, - "redirectUri": { - "default": "http://localhost:9078/lastfm/callback", - "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", - "type": "string" - }, - "secret": { - "description": "Secret generated from Last.fm account", - "type": "string" - }, - "session": { - "description": "Optional session id returned from a completed auth flow", - "type": "string" - } - }, - "required": [ - "apiKey", - "secret" - ], - "type": "object" - }, - "LastfmSourceConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -198,10 +211,10 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonSourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" }, { - "$ref": "#/definitions/LastfmData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData" } ] }, @@ -215,7 +228,7 @@ ], "type": "object" }, - "PlexSourceConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/plex\",{assert:{\"resolution-mode\":\"import\"}}).PlexSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -227,7 +240,7 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonSourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" }, { "properties": { @@ -258,10 +271,7 @@ ], "type": "object" }, - "Record": { - "type": "object" - }, - "SpotifySourceConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -271,7 +281,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/SpotifySourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceData" }, "name": { "description": "Unique identifier for this source.", @@ -283,7 +293,7 @@ ], "type": "object" }, - "SpotifySourceData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceData": { "properties": { "clientId": { "description": "spotify client id", @@ -329,7 +339,7 @@ ], "type": "object" }, - "SubSonicSourceConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubSonicSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -339,7 +349,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/SubsonicData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicData" }, "name": { "description": "Unique identifier for this source.", @@ -351,7 +361,7 @@ ], "type": "object" }, - "SubsonicData": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicData": { "properties": { "maxPollRetries": { "default": 0, @@ -389,7 +399,7 @@ ], "type": "object" }, - "TautulliSourceConfig": { + "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/tautulli\",{assert:{\"resolution-mode\":\"import\"}}).TautulliSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -401,7 +411,7 @@ "data": { "allOf": [ { - "$ref": "#/definitions/CommonSourceData" + "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" }, { "properties": { -- 2.51.2 From 2258bb36b9217cc627cad959619f537d7a5e90d3 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 12:09:20 -0500 Subject: [PATCH 35/64] Refactor: Simplify logs location by using config directory #55 --- src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 80cb8abd..c41e80ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -86,7 +86,10 @@ const logConfig = { } const availableLevels = ['info', 'debug']; -const logPath = process.env.LOG_DIR || path.resolve(projectDir, `./logs`); +let logPath = path.resolve(projectDir, `./logs`); +if(typeof process.env.CONFIG_DIR === 'string') { + logPath = path.resolve(process.env.CONFIG_DIR, './logs'); +} const localUrl = `http://localhost:${port}`; const rotateTransport = new winston.transports.DailyRotateFile({ -- 2.51.2 From 636004a1bba09a7f826e6d3af741ae6fe079b0f7 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 12:12:57 -0500 Subject: [PATCH 36/64] Refactor: Update dockerfile and simplify configuration #55 * Update to alpine 3.17 and node 18 to match the project * Use LSIO base to enable ui/guid usage through env * Use build stages to reduce image size and speed up build * Refactor build to use typescript * Simplify config directory by using root-level /config as default * Add bash script on startup to copy example configs if a new config folder is detected --- .dockerignore | 9 +- .gitignore | 5 +- Dockerfile | 115 ++++++++++++++++++++++---- docker/root/etc/cont-init.d/10-config | 28 +++++++ docker/root/etc/services.d/node/run | 9 ++ 5 files changed, 140 insertions(+), 26 deletions(-) create mode 100644 docker/root/etc/cont-init.d/10-config create mode 100644 docker/root/etc/services.d/node/run diff --git a/.dockerignore b/.dockerignore index b8d32ca8..a99a8e77 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,10 +4,11 @@ npm-debug.log Dockerfile .dockerignore .gitignore +.github .git -config/currentCreds.json *.log -config/maloja.json -config/spotify.json -config/config.json +.idea +config/*.json +config/*.bak /docs +/logs diff --git a/.gitignore b/.gitignore index 70c02601..6e441b25 100644 --- a/.gitignore +++ b/.gitignore @@ -117,13 +117,10 @@ dist .yarn/install-state.gz .pnp.* -*.json +config/*.json *.txt .idea/ -!tsconfig.json -!src/common/infrastructure/schema/*.json - src/**/**.js src/**/**.js.map diff --git a/Dockerfile b/Dockerfile index fb0ff5e2..d7934556 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,35 +1,114 @@ -FROM node:fermium-alpine3.10 +FROM lsiobase/alpine:3.17 as base ENV TZ=Etc/GMT +# borrowed from node/alpine:3.15 +# https://github.com/nodejs/docker-node/blob/main/16/alpine3.15/Dockerfile +# +# Start of node docker stuff +# +ENV NODE_VERSION 18.14.0 + +RUN apk add --no-cache \ + libstdc++ \ + && apk add --no-cache --virtual .build-deps \ + curl \ + && ARCH= && alpineArch="$(apk --print-arch)" \ + && case "${alpineArch##*-}" in \ + x86_64) \ + ARCH='x64' \ + CHECKSUM="b58339afbfa329c94d2764fc7fac4750fc1cbb99c4aa6bf65509200d69e2e3b0" \ + ;; \ + *) ;; \ + esac \ + && if [ -n "${CHECKSUM}" ]; then \ + set -eu; \ + curl -fsSLO --compressed "https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz"; \ + echo "$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" | sha256sum -c - \ + && tar -xJf "node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \ + && ln -s /usr/local/bin/node /usr/local/bin/nodejs; \ + else \ + echo "Building from source" \ + # backup build + && apk add --no-cache --virtual .build-deps-full \ + binutils-gold \ + g++ \ + gcc \ + gnupg \ + libgcc \ + linux-headers \ + make \ + python3 \ + # gpg keys listed at https://github.com/nodejs/node#release-keys + && for key in \ + 4ED778F539E3634C779C87C6D7062848A1AB005C \ + 141F07595B7B3FFE74309A937405533BE57C7D57 \ + 74F12602B6F1C4E913FAA37AD3A89613643B6201 \ + 61FC681DFB92A079F1685E77973F295594EC4689 \ + 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \ + C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ + 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 \ + C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C \ + 108F52B48DB57BB0CC439B2997B01419BD92F80A \ + ; do \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \ + done \ + && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz" \ + && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ + && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ + && grep " node-v$NODE_VERSION.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ + && tar -xf "node-v$NODE_VERSION.tar.xz" \ + && cd "node-v$NODE_VERSION" \ + && ./configure \ + && make -j$(getconf _NPROCESSORS_ONLN) V= \ + && make install \ + && apk del .build-deps-full \ + && cd .. \ + && rm -Rf "node-v$NODE_VERSION" \ + && rm "node-v$NODE_VERSION.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt; \ + fi \ + && rm -f "node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" \ + && apk del .build-deps \ + # smoke tests + && node --version \ + && npm --version +# +# end of docker node stuff +# + RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node +ARG data_dir=/config +VOLUME $data_dir +ENV CONFIG_DIR=$data_dir + +COPY docker/root/ / -WORKDIR /home/node/app +WORKDIR /app -COPY package*.json ./ +FROM base as build -USER node +# copy NPM dependencies and install +COPY --chown=abc:abc package*.json ./ +COPY --chown=abc:abc tsconfig.json . -RUN npm install --production +RUN npm install -COPY --chown=node:node . . +COPY --chown=abc:abc . /app -ENV NPM_CONFIG_LOGLEVEL debug +RUN npm run build && rm -rf node_modules -ARG config_dir=/home/node/config -RUN mkdir -p $config_dir -VOLUME $config_dir -ENV CONFIG_DIR=$config_dir +FROM base as app -ARG log_dir=/home/node/logs -RUN mkdir -p $log_dir -VOLUME $log_dir -ENV LOG_DIR=$log_dir +COPY --from=build --chown=abc:abc /app /app + +RUN npm install --production \ + && npm cache clean --force \ + && chown abc:abc node_modules \ + && rm -rf node_modules/ts-node \ + && rm -rf node_modules/typescript ARG webPort=9078 ENV PORT=$webPort EXPOSE $PORT - -CMD [ "node", "index.js" ] diff --git a/docker/root/etc/cont-init.d/10-config b/docker/root/etc/cont-init.d/10-config new file mode 100644 index 00000000..087f37d6 --- /dev/null +++ b/docker/root/etc/cont-init.d/10-config @@ -0,0 +1,28 @@ +#!/usr/bin/with-contenv bash + +# used https://github.com/linuxserver/docker-plex as a template + +POPULATE_EXAMPLES=false + +echo "Config Directory: ${CONFIG_DIR}" +# make config folder if it does not exist +if [ ! -d "${CONFIG_DIR}" ]; then + echo "${CONFIG_DIR} does not exist! Creating..." + POPULATE_EXAMPLES=true + mkdir -p "${CONFIG_DIR}" +else + if [ "$(ls -A ${CONFIG_DIR})" ]; then + echo "${CONFIG_DIR} is not empty, not creating examples" + else + POPULATE_EXAMPLES=true + fi +fi + +# add example configs +if [ "$POPULATE_EXAMPLES" = true ]; then + echo "${CONFIG_DIR} is empty, adding examples..." + cp -r /app/config/. "${CONFIG_DIR}"/ +fi + +# permissions +chown -R abc:abc "${CONFIG_DIR}" diff --git a/docker/root/etc/services.d/node/run b/docker/root/etc/services.d/node/run new file mode 100644 index 00000000..bcb9f39f --- /dev/null +++ b/docker/root/etc/services.d/node/run @@ -0,0 +1,9 @@ +#!/usr/bin/with-contenv bash + +# used https://github.com/linuxserver/docker-plex as a template + +# NODE_ARGS can be passed by ENV in docker command like "docker run foxxmd/context-mod -e NODE_ARGS=--optimize_for_size" + +exec \ + s6-setuidgid abc \ + /usr/local/bin/node $NODE_ARGS /app/src/index.js run -- 2.51.2 From e2d150ce15ef2384cf798db6ce7491d5336897f7 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 12:44:22 -0500 Subject: [PATCH 37/64] feat: add docker-compose with instructions #55 --- README.md | 52 ++++++++++++++++++++++++++++++++++++---------- docker-compose.yml | 19 +++++++++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 docker-compose.yml diff --git a/README.md b/README.md index 739eec7b..b3a7caa3 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ Clone this repository somewhere and then install from the working directory git clone https://github.com/FoxxMD/multi-scrobbler.git . cd multi-scrobbler npm install +npm build +npm start ``` ### [Docker](https://hub.docker.com/r/foxxmd/multi-scrobbler) @@ -54,39 +56,67 @@ npm install foxxmd/multi-scrobbler:latest ``` +Or use the provided [docker-compose.yml](/docker-compose.yml) after modifying it to fit your configuration. + ## Setup Some setup is required! See the [configuration](docs/configuration.md) docs for a full reference. -### TLDR, Minimal Example +**Is there an example configuration using everything?** -You want to use multi-scrobbler to scrobble your plays from Spotify to Maloja: +Yes, check out the [kitchen sink example](/docs/kitchensink.md) -#### Local +#### Local Example ```bash SPOTIFY_CLIENT_ID=yourId SPOTIFY_CLIENT_SECRET=yourSecret MALOJA_URL=http://domain.tld MALOJA_API_KEY=1234 node index.js ``` +**But I want to use json for configuration?** + +Rename [config.json.example](/config/config.json.example) to `config.json` and modify as necessary. + #### Docker -```bash -docker run -e "SPOTIFY_CLIENT_ID=yourId" -e "SPOTIFY_CLIENT_SECRET=yourSecret" -e "MALOJA_URL=http://domain.tld" -e "MALOJA_API_KEY=1234" -v /path/on/host/config:/home/node/app/config foxxmd/multi-scrobbler -``` +Recommended configuration steps for docker or docker-compose usage: -**But I want to use json for configuration?** +* If using json configuration you must **bind the host directory where your configurations are located into the container:** + * [Using `-v` method for docker](https://docs.docker.com/storage/bind-mounts/#start-a-container-with-a-bind-mount): `-v /path/on/host/config:/config` + * [Using docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#short-syntax-3): `- /path/on/host/config:/config` +* (Optionally) map the web UI port in the container **9078** to the host + * With [docker](https://docs.docker.com/engine/reference/commandline/run/#publish): `-p 9078:9078` (first port is the port on the host to use) + * With [docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#short-syntax-1): `- "9078:9078"` +* (Optionally) set the [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for the container using the environmental variable `TZ` ([docker](https://docs.docker.com/engine/reference/commandline/run/#env)) ([docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#environment)) -Then use [config.json.example](/config/config.json.example) and drop it in your `CONFIG_DIR` directory +##### Linux Host -**Is there an example configuration using everything?** +If you are -Yes, check out the [kitchen sink example](/docs/kitchensink.md) +* using [rootless containers with Podman](https://developers.redhat.com/blog/2020/09/25/rootless-containers-with-podman-the-basics#why_podman_) +* running docker on MacOS or Windows + +this **DOES NOT** apply to you. + +If you are running Docker on a **Linux Host** you must specify `user:group` permissions of the user who owns the **configuration directory** on the host to avoid [docker file permission problems.](https://ikriv.com/blog/?p=4698) These can be specified using the [environmental variables **PUID** and **PGID**.](https://docs.linuxserver.io/general/understanding-puid-and-pgid) + +To get the UID and GID for the current user run these commands from a terminal: + +* `id -u` -- prints UID +* `id -g` -- prints GID + +##### Examples + +```bash +docker run -e "SPOTIFY_CLIENT_ID=yourId" -e "SPOTIFY_CLIENT_SECRET=yourSecret" -e "MALOJA_URL=http://domain.tld" -e "MALOJA_API_KEY=1234" -e "PUID=1000" -e "PGID=1000" -p 9078:9078 -v /path/on/host/config:/home/node/app/config foxxmd/multi-scrobbler +``` + +See the [docker-compose.yml](/docker-compose.yml) file for how to use with docker-compose. ## Usage A status page with statistics, recent logs, and some runtime configuration options can be found at ``` -https://localhost:9078 +http://localhost:9078 ``` Output is also provided to stdout/stderr as well as file if specified in configuration. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..1c1abb7f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +multi-scrobbler: + image: foxxmd/multi-scrobbler + container_name: multi-scrobbler + environment: + - TZ=Etc/GMT # Specify timezone from TZ Database name found here https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + #- SPOTIFY_CLIENT_ID= + #- SPOTIFY_CLIENT_SECRET= + #- SPOTIFY_REDIRECT_URI=http://multi-scrobbler-host-IP:9078/callback ## Need to be whitelisted in Spotify dashboard and is used for creating the connection first time + #- MALOJA_URL=http://maloja:42010 + #- MALOJA_API_KEY= + #- PUID=1000 # required if running docker on linux host, see main README Docker setup instructions + #- PGID=1000 # required if running docker on linux host, see main README Docker setup instructions + volumes: + - /path_on_host/multi-scrobbler-config:/config + #networks: + # - (optional to add container to the same bridge network that maloja is inside to be able to use docker internal networking & dns to resolve and connect to maloja URL via http://maloja:port) + ports: + - 9078:9078 # first port is the HOST port multi-scrobbler will serve UI on + restart: unless-stopped -- 2.51.2 From 176ff80172de5bbda308bb6ed0756ef6bfc56f13 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 12:46:20 -0500 Subject: [PATCH 38/64] Fix docker example folder mapping --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3a7caa3..a79c7bd4 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ To get the UID and GID for the current user run these commands from a terminal: ##### Examples ```bash -docker run -e "SPOTIFY_CLIENT_ID=yourId" -e "SPOTIFY_CLIENT_SECRET=yourSecret" -e "MALOJA_URL=http://domain.tld" -e "MALOJA_API_KEY=1234" -e "PUID=1000" -e "PGID=1000" -p 9078:9078 -v /path/on/host/config:/home/node/app/config foxxmd/multi-scrobbler +docker run -e "SPOTIFY_CLIENT_ID=yourId" -e "SPOTIFY_CLIENT_SECRET=yourSecret" -e "MALOJA_URL=http://domain.tld" -e "MALOJA_API_KEY=1234" -e "PUID=1000" -e "PGID=1000" -p 9078:9078 -v /path/on/host/config:/config foxxmd/multi-scrobbler ``` See the [docker-compose.yml](/docker-compose.yml) file for how to use with docker-compose. -- 2.51.2 From 2b60a0f76f9ae4b1905822523bb18116a2e80a34 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Fri, 10 Feb 2023 13:53:09 -0500 Subject: [PATCH 39/64] fix: Fix ENV usage for jellyfin #61 Correct property names when parsing from ENV --- .../infrastructure/config/source/jellyfin.ts | 30 ++++++++++--------- src/sources/JellyfinSource.ts | 4 +-- src/sources/ScrobbleSources.ts | 8 ++--- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/common/infrastructure/config/source/jellyfin.ts b/src/common/infrastructure/config/source/jellyfin.ts index 39e23ba2..0f8f9606 100644 --- a/src/common/infrastructure/config/source/jellyfin.ts +++ b/src/common/infrastructure/config/source/jellyfin.ts @@ -1,20 +1,22 @@ import {CommonSourceConfig, CommonSourceData} from "./index.js"; +export interface JellyData extends CommonSourceData { + /** + * optional list of users to scrobble tracks from + * + * If none are provided tracks from all users will be scrobbled + * */ + users?: string | string[] + /** + * optional list of servers to scrobble tracks from + * + * If none are provided tracks from all servers will be scrobbled + * */ + servers?: string | string[] +} + export interface JellySourceConfig extends CommonSourceConfig { - data: CommonSourceData & { - /** - * optional list of users to scrobble tracks from - * - * If none are provided tracks from all users will be scrobbled - * */ - user?: string - /** - * optional list of servers to scrobble tracks from - * - * If none are provided tracks from all servers will be scrobbled - * */ - servers?: string - } + data: JellyData } export interface JellySourceAIOConfig extends JellySourceConfig { diff --git a/src/sources/JellyfinSource.ts b/src/sources/JellyfinSource.ts index c8e51ded..e29fece0 100644 --- a/src/sources/JellyfinSource.ts +++ b/src/sources/JellyfinSource.ts @@ -21,7 +21,7 @@ export default class JellyfinSource extends MemorySource { this.users = undefined; } else { if (!Array.isArray(users)) { - this.users = [users]; + this.users = users.split(',') } else { this.users = users; } @@ -32,7 +32,7 @@ export default class JellyfinSource extends MemorySource { this.servers = undefined; } else { if (!Array.isArray(servers)) { - this.servers = [servers]; + this.servers = servers.split(',') } else { this.servers = servers; } diff --git a/src/sources/ScrobbleSources.ts b/src/sources/ScrobbleSources.ts index f742a85f..744e6806 100644 --- a/src/sources/ScrobbleSources.ts +++ b/src/sources/ScrobbleSources.ts @@ -12,7 +12,7 @@ import {Logger} from "winston"; import {SourceAIOConfig, SourceConfig} from "../common/infrastructure/config/source/sources.js"; import {DeezerData, DeezerSourceConfig} from "../common/infrastructure/config/source/deezer.js"; import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm.js"; -import {JellySourceConfig} from "../common/infrastructure/config/source/jellyfin.js"; +import {JellyData, JellySourceConfig} from "../common/infrastructure/config/source/jellyfin.js"; import {SubsonicData, SubSonicSourceConfig} from "../common/infrastructure/config/source/subsonic.js"; import {TautulliSourceConfig} from "../common/infrastructure/config/source/tautulli.js"; import {PlexSourceConfig} from "../common/infrastructure/config/source/plex.js"; @@ -164,9 +164,9 @@ export default class ScrobbleSources { } break; case 'jellyfin': - const j = { - user: process.env.JELLYFIN_USER, - server: process.env.JELLYFIN_SERVER, + const j: JellyData = { + users: process.env.JELLYFIN_USER, + servers: process.env.JELLYFIN_SERVER, }; if (!Object.values(j).every(x => x === undefined)) { configs.push({ -- 2.51.2 From 04c9de648d33b96b3cea35d590278bd336e98ff4 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 14 Feb 2023 10:44:31 -0500 Subject: [PATCH 40/64] refactor: Update s6-overlay usage for ms config and start up Refactor s6 usage based on most recent lsio images --- Dockerfile | 4 ++-- .../init-ms-config/dependencies.d/init-config | 0 .../s6-rc.d/init-ms-config/run} | 13 +++++++++---- .../root/etc/s6-overlay/s6-rc.d/init-ms-config/type | 1 + .../root/etc/s6-overlay/s6-rc.d/init-ms-config/up | 1 + .../s6-rc.d/svc-node/dependencies.d/init-services | 0 docker/root/etc/s6-overlay/s6-rc.d/svc-node/run | 8 ++++++++ docker/root/etc/s6-overlay/s6-rc.d/svc-node/type | 1 + .../s6-rc.d/user/contents.d/init-ms-config | 0 .../etc/s6-overlay/s6-rc.d/user/contents.d/svc-node | 0 docker/root/etc/services.d/node/run | 9 --------- 11 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/dependencies.d/init-config rename docker/root/etc/{cont-init.d/10-config => s6-overlay/s6-rc.d/init-ms-config/run} (54%) mode change 100644 => 100755 create mode 100644 docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/type create mode 100644 docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/up create mode 100644 docker/root/etc/s6-overlay/s6-rc.d/svc-node/dependencies.d/init-services create mode 100755 docker/root/etc/s6-overlay/s6-rc.d/svc-node/run create mode 100644 docker/root/etc/s6-overlay/s6-rc.d/svc-node/type create mode 100644 docker/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-ms-config create mode 100644 docker/root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-node delete mode 100644 docker/root/etc/services.d/node/run diff --git a/Dockerfile b/Dockerfile index d7934556..d7571143 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,8 @@ FROM lsiobase/alpine:3.17 as base ENV TZ=Etc/GMT -# borrowed from node/alpine:3.15 -# https://github.com/nodejs/docker-node/blob/main/16/alpine3.15/Dockerfile +# borrowed from node/alpine:3.17 +# https://github.com/nodejs/docker-node/blob/main/18/alpine3.17/Dockerfile # # Start of node docker stuff # diff --git a/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/dependencies.d/init-config b/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/dependencies.d/init-config new file mode 100644 index 00000000..e69de29b diff --git a/docker/root/etc/cont-init.d/10-config b/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/run old mode 100644 new mode 100755 similarity index 54% rename from docker/root/etc/cont-init.d/10-config rename to docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/run index 087f37d6..521bc927 --- a/docker/root/etc/cont-init.d/10-config +++ b/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/run @@ -4,15 +4,17 @@ POPULATE_EXAMPLES=false -echo "Config Directory: ${CONFIG_DIR}" +echo "-------------------------------------" +echo -e "Setting up multi-scrobbler config directory based on CONFIG_DIR env: ${CONFIG_DIR}\n" + # make config folder if it does not exist if [ ! -d "${CONFIG_DIR}" ]; then - echo "${CONFIG_DIR} does not exist! Creating..." + echo "Directory does not exist! Creating..." POPULATE_EXAMPLES=true mkdir -p "${CONFIG_DIR}" else if [ "$(ls -A ${CONFIG_DIR})" ]; then - echo "${CONFIG_DIR} is not empty, not creating examples" + echo "Directory is not empty, not creating examples." else POPULATE_EXAMPLES=true fi @@ -20,9 +22,12 @@ fi # add example configs if [ "$POPULATE_EXAMPLES" = true ]; then - echo "${CONFIG_DIR} is empty, adding examples..." + echo "Directory is empty, adding examples..." cp -r /app/config/. "${CONFIG_DIR}"/ fi # permissions +echo "chown'ing directory to ensure correct permissions." chown -R abc:abc "${CONFIG_DIR}" +echo "Done!" +echo -e "-------------------------------------\n" diff --git a/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/type b/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/type new file mode 100644 index 00000000..bdd22a18 --- /dev/null +++ b/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/up b/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/up new file mode 100644 index 00000000..13ff778b --- /dev/null +++ b/docker/root/etc/s6-overlay/s6-rc.d/init-ms-config/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-ms-config/run diff --git a/docker/root/etc/s6-overlay/s6-rc.d/svc-node/dependencies.d/init-services b/docker/root/etc/s6-overlay/s6-rc.d/svc-node/dependencies.d/init-services new file mode 100644 index 00000000..e69de29b diff --git a/docker/root/etc/s6-overlay/s6-rc.d/svc-node/run b/docker/root/etc/s6-overlay/s6-rc.d/svc-node/run new file mode 100755 index 00000000..60b99443 --- /dev/null +++ b/docker/root/etc/s6-overlay/s6-rc.d/svc-node/run @@ -0,0 +1,8 @@ +#!/usr/bin/with-contenv bash + +# used https://github.com/linuxserver/docker-wikijs/blob/master/root/etc/s6-overlay/s6-rc.d/svc-wikijs/run as a template + +# NODE_ARGS can be passed by ENV in docker command like "docker run foxxmd/multi-scrobbler -e NODE_ARGS=--optimize_for_size" +echo -e "\nmulti-scrobbler is starting!" +exec \ + s6-setuidgid abc /usr/local/bin/node $NODE_ARGS /app/src/index.js run diff --git a/docker/root/etc/s6-overlay/s6-rc.d/svc-node/type b/docker/root/etc/s6-overlay/s6-rc.d/svc-node/type new file mode 100644 index 00000000..5883cff0 --- /dev/null +++ b/docker/root/etc/s6-overlay/s6-rc.d/svc-node/type @@ -0,0 +1 @@ +longrun diff --git a/docker/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-ms-config b/docker/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-ms-config new file mode 100644 index 00000000..e69de29b diff --git a/docker/root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-node b/docker/root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-node new file mode 100644 index 00000000..e69de29b diff --git a/docker/root/etc/services.d/node/run b/docker/root/etc/services.d/node/run deleted file mode 100644 index bcb9f39f..00000000 --- a/docker/root/etc/services.d/node/run +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/with-contenv bash - -# used https://github.com/linuxserver/docker-plex as a template - -# NODE_ARGS can be passed by ENV in docker command like "docker run foxxmd/context-mod -e NODE_ARGS=--optimize_for_size" - -exec \ - s6-setuidgid abc \ - /usr/local/bin/node $NODE_ARGS /app/src/index.js run -- 2.51.2 From 1cbad9ccfd09d06c694414a69fbf4b0f7c9a00c0 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 14 Feb 2023 11:23:05 -0500 Subject: [PATCH 41/64] refactor: Use node from alpine packages instead of building from source Using LTS either way but building from source makes image much larger and takes forever on build runners (github) --- Dockerfile | 90 ++++--------------- .../root/etc/s6-overlay/s6-rc.d/svc-node/run | 2 +- package.json | 2 +- 3 files changed, 17 insertions(+), 77 deletions(-) diff --git a/Dockerfile b/Dockerfile index d7571143..0eda7d88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,80 +2,18 @@ FROM lsiobase/alpine:3.17 as base ENV TZ=Etc/GMT -# borrowed from node/alpine:3.17 -# https://github.com/nodejs/docker-node/blob/main/18/alpine3.17/Dockerfile -# -# Start of node docker stuff -# -ENV NODE_VERSION 18.14.0 - -RUN apk add --no-cache \ - libstdc++ \ - && apk add --no-cache --virtual .build-deps \ - curl \ - && ARCH= && alpineArch="$(apk --print-arch)" \ - && case "${alpineArch##*-}" in \ - x86_64) \ - ARCH='x64' \ - CHECKSUM="b58339afbfa329c94d2764fc7fac4750fc1cbb99c4aa6bf65509200d69e2e3b0" \ - ;; \ - *) ;; \ - esac \ - && if [ -n "${CHECKSUM}" ]; then \ - set -eu; \ - curl -fsSLO --compressed "https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz"; \ - echo "$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" | sha256sum -c - \ - && tar -xJf "node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \ - && ln -s /usr/local/bin/node /usr/local/bin/nodejs; \ - else \ - echo "Building from source" \ - # backup build - && apk add --no-cache --virtual .build-deps-full \ - binutils-gold \ - g++ \ - gcc \ - gnupg \ - libgcc \ - linux-headers \ - make \ - python3 \ - # gpg keys listed at https://github.com/nodejs/node#release-keys - && for key in \ - 4ED778F539E3634C779C87C6D7062848A1AB005C \ - 141F07595B7B3FFE74309A937405533BE57C7D57 \ - 74F12602B6F1C4E913FAA37AD3A89613643B6201 \ - 61FC681DFB92A079F1685E77973F295594EC4689 \ - 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \ - C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ - 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 \ - C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C \ - 108F52B48DB57BB0CC439B2997B01419BD92F80A \ - ; do \ - gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \ - gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \ - done \ - && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz" \ - && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ - && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ - && grep " node-v$NODE_VERSION.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ - && tar -xf "node-v$NODE_VERSION.tar.xz" \ - && cd "node-v$NODE_VERSION" \ - && ./configure \ - && make -j$(getconf _NPROCESSORS_ONLN) V= \ - && make install \ - && apk del .build-deps-full \ - && cd .. \ - && rm -Rf "node-v$NODE_VERSION" \ - && rm "node-v$NODE_VERSION.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt; \ - fi \ - && rm -f "node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" \ - && apk del .build-deps \ - # smoke tests - && node --version \ - && npm --version -# -# end of docker node stuff -# +RUN \ + echo "**** install build packages ****" && \ + apk add --no-cache \ + alpine-base \ + git \ + nodejs \ + npm \ + openssh && \ + echo "**** cleanup ****" && \ + rm -rf \ + /root/.cache \ + /tmp/* RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone @@ -103,7 +41,9 @@ FROM base as app COPY --from=build --chown=abc:abc /app /app -RUN npm install --production \ +ENV NODE_ENV="production" + +RUN npm install --omit=dev \ && npm cache clean --force \ && chown abc:abc node_modules \ && rm -rf node_modules/ts-node \ diff --git a/docker/root/etc/s6-overlay/s6-rc.d/svc-node/run b/docker/root/etc/s6-overlay/s6-rc.d/svc-node/run index 60b99443..f36cfe8b 100755 --- a/docker/root/etc/s6-overlay/s6-rc.d/svc-node/run +++ b/docker/root/etc/s6-overlay/s6-rc.d/svc-node/run @@ -5,4 +5,4 @@ # NODE_ARGS can be passed by ENV in docker command like "docker run foxxmd/multi-scrobbler -e NODE_ARGS=--optimize_for_size" echo -e "\nmulti-scrobbler is starting!" exec \ - s6-setuidgid abc /usr/local/bin/node $NODE_ARGS /app/src/index.js run + s6-setuidgid abc /usr/bin/node $NODE_ARGS /app/src/index.js run diff --git a/package.json b/package.json index 10494a15..cad9486b 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "engines": { "node": ">=18.0.0", - "npm": ">=9.3.0" + "npm": ">=9.1.0" }, "repository": { "type": "git", -- 2.51.2 From 1f6996d881fe8edeaf22d129e47835cbd268ab17 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 14 Feb 2023 11:35:44 -0500 Subject: [PATCH 42/64] chore: Update superagent version --- package-lock.json | 64 ++++++++++++++++++++++++++++------------------- package.json | 7 +++++- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 295eaca8..cd393061 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "safe-stable-stringify": "^1.1.1", "socket.io": "4.1.3", "spotify-web-api-node": "^5.0.2", - "superagent": "^6.1.0", + "superagent": "^8.0.9", "triple-beam": "^1.3.0", "winston": "^3.3.3", "winston-daily-rotate-file": "^4.5.0", @@ -54,7 +54,7 @@ }, "engines": { "node": ">=18.0.0", - "npm": ">=9.3.0" + "npm": ">=9.1.0" } }, "node_modules/@awaitjs/express": { @@ -229,9 +229,9 @@ } }, "node_modules/@types/express-session": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz", - "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.6.tgz", + "integrity": "sha512-L6sB04HVA4HEZo1hDL65JXdZdBJtzZnCiw/P7MnO4w6746tJCNtXlHtzEASyI9ccn9zyOw6IbqQuhVa03VpO4w==", "dev": true, "dependencies": { "@types/express": "*" @@ -1027,9 +1027,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -1923,25 +1923,23 @@ } }, "node_modules/superagent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", - "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", - "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", + "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", "dependencies": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.7", - "form-data": "^3.0.0", - "formidable": "^1.2.2", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", "methods": "^1.1.2", - "mime": "^2.4.6", - "qs": "^6.9.4", - "readable-stream": "^3.6.0", - "semver": "^7.3.2" + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" }, "engines": { - "node": ">= 7.0.0" + "node": ">=6.4.0 <13 || >=14" } }, "node_modules/superagent/node_modules/debug": { @@ -1960,11 +1958,25 @@ } } }, + "node_modules/superagent/node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "node_modules/superagent/node_modules/formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", - "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } diff --git a/package.json b/package.json index cad9486b..8122d89b 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "safe-stable-stringify": "^1.1.1", "socket.io": "4.1.3", "spotify-web-api-node": "^5.0.2", - "superagent": "^6.1.0", + "superagent": "^8.0.9", "triple-beam": "^1.3.0", "winston": "^3.3.3", "winston-daily-rotate-file": "^4.5.0", @@ -76,5 +76,10 @@ "tsconfig-paths": "^3.13.0", "typescript": "^4.9.5", "typescript-json-schema": "~0.55" + }, + "overrides": { + "spotify-web-api-node": { + "superagent": "$superagent" + } } } -- 2.51.2 From 700722627a9e98170f185735ea56a6141b0cf879 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 20 Feb 2023 09:42:16 -0500 Subject: [PATCH 43/64] feat: Allow parsing json files as json5 So users can use example json files with comments as-is --- package-lock.json | 26 +++++++++++++++++++------- package.json | 3 ++- src/utils.ts | 3 ++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index cd393061..fd8393ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "express": "^4.17.1", "express-session": "^1.17.2", "formidable": "^3.2.4", + "json5": "^2.2.3", "lastfm-node-client": "^2.2.0", "passport": "^0.5.0", "passport-deezer": "^0.2.0", @@ -1256,15 +1257,14 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/kuler": { @@ -2090,6 +2090,18 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/package.json b/package.json index 8122d89b..79ff285d 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "express": "^4.17.1", "express-session": "^1.17.2", "formidable": "^3.2.4", + "json5": "^2.2.3", "lastfm-node-client": "^2.2.0", "passport": "^0.5.0", "passport-deezer": "^0.2.0", @@ -79,7 +80,7 @@ }, "overrides": { "spotify-web-api-node": { - "superagent": "$superagent" + "superagent": "$superagent" } } } diff --git a/src/utils.ts b/src/utils.ts index a3037427..8276e5dc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,6 +3,7 @@ import dayjs, {Dayjs} from 'dayjs'; import utc from 'dayjs/plugin/utc.js'; import winston, {Logger} from "winston"; import stringify from 'safe-stable-stringify'; +import JSON5 from 'json5'; import { TimeoutError, WebapiError } from "spotify-web-api-node/src/response-error.js"; import { Response } from 'superagent'; import Ajv, {Schema} from 'ajv'; @@ -17,7 +18,7 @@ export async function readJson(this: any, path: any, {logErrors = true, throwOnN try { await promises.access(path, constants.R_OK); const data = await promises.readFile(path); - return JSON.parse(data as unknown as string); + return JSON5.parse(data as unknown as string); } catch (e) { const {code} = e; if (code === 'ENOENT') { -- 2.51.2 From 193a5c1e4da4a9656e65de67e6be1865b105f7e3 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 20 Feb 2023 11:18:01 -0500 Subject: [PATCH 44/64] docs: Rewrite docs to include more examples and clearer usage --- README.md | 83 ++++------------ docs/configuration.md | 219 ++++++++++++++++++++++++++++++------------ docs/installation.md | 80 +++++++++++++++ 3 files changed, 256 insertions(+), 126 deletions(-) create mode 100644 docs/installation.md diff --git a/README.md b/README.md index a79c7bd4..abca0da7 100644 --- a/README.md +++ b/README.md @@ -31,85 +31,40 @@ A javascript app to scrobble plays from multiple sources to [Maloja](https://git **But I already scrobble my music to Last.fm, is multi-scrobbler for me?** -Yes! You can use [Last.fm as a Source](/docs/configuration.md#lastfm-source) to mirror scrobbles from your Last.fm profile to Maloja. That way you can keep your current scrobble setup as-is but still get the benefit of capturing your data to a self-hosted location. +Yes! You can use [Last.fm as a **Source**](/docs/configuration.md#lastfm--source-) to mirror scrobbles from your Last.fm profile to Maloja. That way you can keep your current scrobble setup as-is but still get the benefit of capturing your data to a self-hosted location. -## Installation - - -### Locally +## How Does multi-scrobbler (MS) Work? -Clone this repository somewhere and then install from the working directory +You set up configurations for one or more **Sources** and one or more **Clients**. MS monitors all of your configured **Sources**. When new tracks are played by a Source it grabs that information and then sends it (scrobbles it) to all **Clients** that Source is configured to scrobble to. -```bash -git clone https://github.com/FoxxMD/multi-scrobbler.git . -cd multi-scrobbler -npm install -npm build -npm start -``` +### Source -### [Docker](https://hub.docker.com/r/foxxmd/multi-scrobbler) +A **Source** is a data source that contains information about tracks you are playing like a music player or platform. Examples are **Spotify, Jellyfin, Plex, Airsonic**, etc... -``` -foxxmd/multi-scrobbler:latest -``` +Source configurations consist of: -Or use the provided [docker-compose.yml](/docker-compose.yml) after modifying it to fit your configuration. +* A friendly name. +* Any data needed to communicate or authenticate with the Source. +* An optional list of Client names that the Source should scrobble to. If omitted the Source also scrobbles to all configured Clients. -## Setup - -Some setup is required! See the [configuration](docs/configuration.md) docs for a full reference. - -**Is there an example configuration using everything?** - -Yes, check out the [kitchen sink example](/docs/kitchensink.md) - -#### Local Example -```bash -SPOTIFY_CLIENT_ID=yourId SPOTIFY_CLIENT_SECRET=yourSecret MALOJA_URL=http://domain.tld MALOJA_API_KEY=1234 node index.js -``` +### Client -**But I want to use json for configuration?** +A **Client** is an application that stores the historical information about what songs you have played (scrobbles). Examples are **Maloja, Last.fm, Listenbrainz**... -Rename [config.json.example](/config/config.json.example) to `config.json` and modify as necessary. +Client configurations consist of: -#### Docker +* A friendly name. +* Any data needed to communicate or authenticate with the Client. -Recommended configuration steps for docker or docker-compose usage: - -* If using json configuration you must **bind the host directory where your configurations are located into the container:** - * [Using `-v` method for docker](https://docs.docker.com/storage/bind-mounts/#start-a-container-with-a-bind-mount): `-v /path/on/host/config:/config` - * [Using docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#short-syntax-3): `- /path/on/host/config:/config` -* (Optionally) map the web UI port in the container **9078** to the host - * With [docker](https://docs.docker.com/engine/reference/commandline/run/#publish): `-p 9078:9078` (first port is the port on the host to use) - * With [docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#short-syntax-1): `- "9078:9078"` -* (Optionally) set the [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for the container using the environmental variable `TZ` ([docker](https://docs.docker.com/engine/reference/commandline/run/#env)) ([docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#environment)) - -##### Linux Host - -If you are - -* using [rootless containers with Podman](https://developers.redhat.com/blog/2020/09/25/rootless-containers-with-podman-the-basics#why_podman_) -* running docker on MacOS or Windows - -this **DOES NOT** apply to you. - -If you are running Docker on a **Linux Host** you must specify `user:group` permissions of the user who owns the **configuration directory** on the host to avoid [docker file permission problems.](https://ikriv.com/blog/?p=4698) These can be specified using the [environmental variables **PUID** and **PGID**.](https://docs.linuxserver.io/general/understanding-puid-and-pgid) - -To get the UID and GID for the current user run these commands from a terminal: - -* `id -u` -- prints UID -* `id -g` -- prints GID +## Installation -##### Examples +[See the **Installation** documentation](/docs/installation.md) -```bash -docker run -e "SPOTIFY_CLIENT_ID=yourId" -e "SPOTIFY_CLIENT_SECRET=yourSecret" -e "MALOJA_URL=http://domain.tld" -e "MALOJA_API_KEY=1234" -e "PUID=1000" -e "PGID=1000" -p 9078:9078 -v /path/on/host/config:/config foxxmd/multi-scrobbler -``` +## Configuration -See the [docker-compose.yml](/docker-compose.yml) file for how to use with docker-compose. +[See the **Configuration** documentation](/docs/configuration.md) ## Usage @@ -120,7 +75,7 @@ http://localhost:9078 ``` Output is also provided to stdout/stderr as well as file if specified in configuration. -On first startup you may need to authorize Spotify by visiting the callback URL (which can also be accessed from the status page). Visit the status page above to find the applicable link to trigger this. +On first startup you may need to authorize Spotify and/or Last.fm by visiting the callback URL (which can also be accessed from the status page). Visit the status page above to find the applicable link to trigger this. ## License diff --git a/docs/configuration.md b/docs/configuration.md index d0587e6e..f230c04e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,28 +1,93 @@ -# General +* [Configuration Overview](#configuration-overview) + * [ENV-Based Configuration](#env-based-configuration) + * [File-Based Configuration](#file-based-configuration) + * [All-in-One File Configuration](#all-in-one-file-configuration) + * [Specific File Configuration](#specific-file-configuration) +* [Source Configurations](#source-configurations) + * [Spotify](#spotify) + * [ENV-Based](#env-based) + * [JSON-Based](#json-based) + * [Plex](#plex) + * [ENV-Based](#env-based-1) + * [JSON-Based](#json-based-1) + * [Tautulli](#tautulli) + * [ENV-Based](#env-based-2) + * [JSON-Based](#json-based-2) + * [Subsonic](#subsonic) + * [ENV-Based](#env-based-3) + * [JSON-Based](#json-based-3) + * [Jellyfin](#jellyfin) + * [ENV-Based](#env-based-4) + * [JSON-Based](#json-based-4) + * [Last.fm (Source)](#lastfm--source-) + * [ENV-Based](#env-based-5) + * [JSON-Based](#json-based-5) + * [Deezer](#deezer) + * [ENV-Based](#env-based-6) + * [JSON-Based](#json-based-6) +* [Client Configurations](#client-configurations) + * [Maloja](#maloja) + * [ENV-Based](#env-based-7) + * [JSON-Based](#json-based-7) + * [Last.fm](#lastfm) + * [ENV-Based](#env-based-8) + * [JSON-Based](#json-based-8) + +# Configuration Overview + +[**Sources** and **Clients**](/README.md#how-does-multi-scrobbler--ms--work) are configured using environmental (ENV) variables and/or json files. + +**MS will parse configuration from both configuration types.** You can mix and match configurations but it is generally better to stick to one or the other. + +## ENV-Based Configuration + +This is done by passing environmental variables and so does not require any files to run MS. + +* Using a docker container EX `docker run -e "SPOTIFY_CLIENT_ID=yourId" -e "SPOTIFY_CLIENT_SECRET=yourSecret" ...` +* Using a local installations by exporting variables before running MS EX `SPOTIFY_CLIENT_ID=yourId SPOTIFY_CLIENT_SECRET=yourSecret node index.js` + +Use ENV-based configuration if: + +* You are the only person for whom MS is scrobbling for +* You have a very simple setup for MS such as one scrobble [Client](/README.md#client) and one [Source](/README.md#source) IE Plex -> Maloja + +## File-Based Configuration + +MS will parse configuration files located in the directory specified by the `CONFIG_DIR` environmental variable. This variable defaults to: + +* Local installation -> `PROJECT_DIR/config` +* Docker -> `/config` (in the container) -- see the [install docs](/docs/installation.md#docker) for how to configure this correctly + +Use File-based configuration if: + +* You have many [Sources](/README.md#source) +* You have many of each type of **Source** you want to scrobble from IE 2x Plex accounts, 3x Spotify accounts, 1x + Funkwhale... +* You have more than one scrobble **Client** you want to scrobble to IE multiple Maloja servers +* You want only some **Sources** to scrobble to some **Clients** IE Fred's Spotify account scrobbles to Fred's Maloja + server, but not Mary's Maloja server -General configuration options. These must be set through environmental variables because they affect initial startup of -the app. **These variables are also available to Docker containers.** +File-based configurations located in the `CONFIG_DIR` directory can be parsed from -| Environmental Variable | Required? | Default | Description | -|----------------------------|-----------|--------------|-------------------------------------------------------------------------------------------| -| `CONFIG_DIR` | No | `CWD/config` | Directory to look for all other configuration files | -| `LOG_PATH` | No | `CWD/logs` | If `false` no logs will be written. If `string` will be the directory logs are written to | -| `PORT` | No | 9078 | Port to run web server on | +* an **all-in-one** config file named `config.json` that contains information for all Sources and Clients and/or +* many **specific** files named based on the client/source to configure IE `plex.json` `spotify.json` -**The app must have permission to write to `CONFIG_DIR` in order to store the current spotify access token.** +There are **example configurations** for all Source/Client types and AIO config located in the [/config](/config) directory of this project. These can be used as-is by renaming them to `.json`. +For docker installations these examples are copied to your configuration directory on first-time use. +There is also a [**kitchensink example**](/docs/kitchensink.md) that provides examples of using all sources/clients in a complex configuration. -# Sources and (Scrobble) Clients +### All-in-One File Configuration -The app has two types of configurations: +Example directory structure: -* **Sources** -- Where plays are parsed from -* **Clients** -- Scrobble clients that plays are scrobbled to +``` +/CONFIG_DIR + config.json +``` -All configurations can be configured through: -* environmental variables -* individual **json** files for each source/client type found in the `CONFIG_DIR` directory IE `config/spotify.json` -* or through the main `config.json` (also found in `CONFIG_DIR` directory) using the `clients` or `sources` property under `data`: +
+Config Example ```json5 // in config.json @@ -40,38 +105,68 @@ All configurations can be configured through: //... } } + ], + "clients": [ + { + "name": "myFirstMalojaClient", + "type": "maloja", + "data": { + "url": "http://myMalojaServer.example", + // ... + } + } ] } ``` -See [config.json.example](../config/config.json.example) for a short example of this or check out [the kitchen sink example](kitchensink.md). +
-### ENV-Based or JSON-Based? +`config.json` can also be used to set default behavior for all sources/clients using `sourceDefaults` and `clientDefaults` properties. -multi-scrobbler can be configured differently depending on how you will use it. See which use-case fits you the best and then use that approach when setting up each configuration: +See [config.json.example](/config/config.json.example) for an annotated example or check out [the kitchen sink example](kitchensink.md). -#### ENV-Based (Single User) +### Specific File Configuration -* You are the only person for whom the application is scrobbling -* You may have many sources (Plex, Spotify, Tautulli...) but you only have one of each type of source -* You have only one scrobble client -* **Easier for small setups. Difficult for larger, multi-sourced setups (may want to switch to json)** -* **Will not work for multi-user setups** +Each file is named by the **type** of the Client/Source found in below sections. Each file as an **array** of that type of Client/Source. -#### JSON-Based (Multi User) +Example directory structure: -* You are a single user but want to set up many sources -* You want to use multi-scrobbler to scrobble for yourself and others IE family, friends, etc. -* You may have many of each type of **Source** you want to scrobble from IE 2x Plex accounts, 3x Spotify accounts, 1x - Funkwhale... -* You have more than one scrobble **Client** you want to scrobble to IE multiple Maloja servers, one for each person -* You want only some **Sources** to scrobble to some **Clients** IE Fred's Spotify account scrobbles to Fred's Maloja - server, but not Mary's Maloja server +``` +/CONFIG_DIR + plex.json + spotify.json + maloja.json +``` -Note: While you may mix and match configuration approaches it is recommended to **only use ENV-based configs if you are -doing everything in ENV-based configurations.** +
+Config Example -# Sources +```json5 +// in maloja.json +[ + { + "name": "myFirstMalojaClient", + "data": { + "url": "http://myMalojaServer.example", + "apiKey": "myKey" + } + }, + { + "name": "mySecondMalojaClient", + "data": { + "url": "http://my2ndMalojaServer.example", + "apiKey": "myKey" + } + } +] + +``` + +
+ +See the [/config](/config) directory of this project for examples of each type of config file or reference specific files below. + +# Source Configurations ## [Spotify](https://www.spotify.com) @@ -80,17 +175,17 @@ Client ID/Secret. Make sure to also whitelist your redirect URI in the applicati ### ENV-Based -| Environmental Variable | Required? | Default | Description | +| Environmental Variable | Required? | Default | Description | |----------------------------|-----------|----------------------------------|----------------------------------------------------| | `SPOTIFY_CLIENT_ID` | Yes | | | | `SPOTIFY_CLIENT_SECRET` | Yes | | | | `SPOTIFY_ACCESS_TOKEN` | No | | Must include either this token or client id/secret | | `SPOTIFY_REFRESH_TOKEN` | No | | If using access token this is also recommended | -| `SPOTIFY_REDIRECT_URI` | No | `http://localhost:{PORT}/callback` | URI must end in `callback` | +| `SPOTIFY_REDIRECT_URI` | No | `http://localhost:9078/callback` | URI must end in `callback` | ### JSON-Based -See [`spotify.json.example`](../config/spotify.json.example) +See [`spotify.json.example`](/config/spotify.json.example) ## [Plex](https://plex.tv) @@ -104,7 +199,7 @@ Check the [instructions](plex.md) on how to setup a [webhooks](https://support.p ### JSON-Based -See [`plex.json.example`](../config/plex.json.example) +See [`plex.json.example`](/config/plex.json.example) ## [Tautulli](https://tautulli.com) @@ -118,7 +213,7 @@ Check the [instructions](plex.md) on how to setup a notification agent. ### JSON-Based -See [`tautulli.json.example`](../config/tautulli.json.example) +See [`tautulli.json.example`](/config/tautulli.json.example) ## [Subsonic](http://www.subsonic.org/) @@ -141,7 +236,7 @@ Can use this source for any application that implements the [Subsonic API](http: ### JSON-Based -See [`subsonic.json.example`](../config/subsonic.json.example) +See [`subsonic.json.example`](/config/subsonic.json.example) ## [Jellyfin](https://jellyfin.org/) @@ -169,7 +264,7 @@ Must be using Jellyfin 10.7 or greater ### JSON-Based -See [`jellyfin.json.example`](../config/jellyfin.json.example) +See [`jellyfin.json.example`](/config/jellyfin.json.example) ## [Last.fm (Source)](https://www.last.fm) @@ -181,7 +276,7 @@ No support for ENV based for Last.fm as a client (only source) ### JSON-Based -See [`lastfm.json.example`](../config/lastfm.json.example), change `configureAs` to `source`. +See [`lastfm.json.example`](/config/lastfm.json.example), change `configureAs` to `source`. ## [Deezer](https://deezer.com/) @@ -197,24 +292,24 @@ After application creation you should have credentials displayed in the "My Apps * **Secret Key** * **Redirect URL** (if not the default) -### If no access token is provided... +**If no access token is provided...** After starting multi-scrobbler with credentials in-place open the dashboard (`http://localhost:9078`) and find your Deezer source. Click **(Re)authenticate and (re)start polling** to start the login process. After login is complete polling will begin automatically. ### ENV-Based -| Environmental Variable | Required? | Default | Description | -|----------------------------|-----------|----------------------------------|----------------------------------------------------| -| `DEEZER_CLIENT_ID` | Yes | | Your **Application ID** | -| `DEEZER_CLIENT_SECRET` | Yes | | Your **Secret Key** | -| `DEEZER_ACCESS_TOKEN` | No | | Must include either this token or client id/secret | -| `DEEZER_REDIRECT_URI` | No | `http://localhost:{PORT}/deezer/callback` | URI must end in `deezer/callback` | +| Environmental Variable | Required? | Default | Description | +|----------------------------|-----------|-----------------------------------------|----------------------------------------------------| +| `DEEZER_CLIENT_ID` | Yes | | Your **Application ID** | +| `DEEZER_CLIENT_SECRET` | Yes | | Your **Secret Key** | +| `DEEZER_ACCESS_TOKEN` | No | | Must include either this token or client id/secret | +| `DEEZER_REDIRECT_URI` | No | `http://localhost:9078/deezer/callback` | URI must end in `deezer/callback` | ### JSON-Based -See [`deezer.json.example`](../config/deezer.json.example) +See [`deezer.json.example`](/config/deezer.json.example) -# Clients +# Client Configurations ## [Maloja](https://github.com/krateng/maloja) @@ -227,7 +322,7 @@ See [`deezer.json.example`](../config/deezer.json.example) ### JSON-Based -See [`maloja.json.example`](../config/maloja.json.example) +See [`maloja.json.example`](/config/maloja.json.example) ## [Last.fm](https://www.last.fm) @@ -241,13 +336,13 @@ or replace `localhost:9078` with your own base URL ### ENV-Based -| Environmental Variable | Required? | Default | Description | -|----------------------------|-----------|---------|-------------------------------| -| `LASTFM_API_KEY` | Yes | | Api Key from your API Account | -| `LASTFM_SECRET` | Yes | | Shared secret from your API Account | -| `LASTFM_REDIRECT_URI` | No | `http://localhost:{PORT}/lastfm/callback` | Url to use for authentication. Must include `lastfm/callback` somewhere in it | -| `LASTFM_SESSION` | No | | Session id. Will be generated by authentication flow if not provided. | +| Environmental Variable | Required? | Default | Description | +|----------------------------|-----------|-----------------------------------------|-------------------------------| +| `LASTFM_API_KEY` | Yes | | Api Key from your API Account | +| `LASTFM_SECRET` | Yes | | Shared secret from your API Account | +| `LASTFM_REDIRECT_URI` | No | `http://localhost:9078/lastfm/callback` | Url to use for authentication. Must include `lastfm/callback` somewhere in it | +| `LASTFM_SESSION` | No | | Session id. Will be generated by authentication flow if not provided. | ### JSON-Based -See [`lastfm.json.example`](../config/lastfm.json.example) +See [`lastfm.json.example`](/config/lastfm.json.example) diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 00000000..e12a7153 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,80 @@ +# Installation + +## Local + +Clone this repository somewhere and then install from the working directory + +```bash +git clone https://github.com/FoxxMD/multi-scrobbler.git . +cd multi-scrobbler +nvm use # optional, to set correct Node version +npm install +npm build +npm start +``` + +### Local Usage Examples + +* The web UI is served on port `9078`. This can be modified using the `PORT` environmental variable. + +#### Using [file-based](/docs/configuration.md#file-based-configuration) configuration + +```bash +npm start +``` + +#### Using [env-based](/docs/configuration.md#env-based-configuration) configuration + +```bash +SPOTIFY_CLIENT_ID=yourId SPOTIFY_CLIENT_SECRET=yourSecret MALOJA_URL="http://domain.tld" node src/index.js +``` + +## [Docker](https://hub.docker.com/r/foxxmd/multi-scrobbler) + +``` +foxxmd/multi-scrobbler:latest +``` + +Or use the provided [docker-compose.yml](/docker-compose.yml) after modifying it to fit your configuration. + +Recommended configuration steps for docker or docker-compose usage: + +* If you must **bind a host directory into the container for storing configurations and credentials:** + * [Using `-v` method for docker](https://docs.docker.com/storage/bind-mounts/#start-a-container-with-a-bind-mount): `-v /path/on/host/config:/config` + * [Using docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#short-syntax-3): `- /path/on/host/config:/config` +* (Optionally) map the web UI port in the container **9078** to the host + * With [docker](https://docs.docker.com/engine/reference/commandline/run/#publish): `-p 9078:9078` (first port is the port on the host to use) + * With [docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#short-syntax-1): `- "9078:9078"` +* (Optionally) set the [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for the container using the environmental variable `TZ` ([docker](https://docs.docker.com/engine/reference/commandline/run/#env)) ([docker-compose](https://docs.docker.com/compose/compose-file/compose-file-v3/#environment)) + +### Linux Host + +If you are + +* using [rootless containers with Podman](https://developers.redhat.com/blog/2020/09/25/rootless-containers-with-podman-the-basics#why_podman_) +* running docker on MacOS or Windows + +this **DOES NOT** apply to you. + +If you are running Docker on a **Linux Host** you must specify `user:group` permissions of the user who owns the **configuration directory** on the host to avoid [docker file permission problems.](https://ikriv.com/blog/?p=4698) These can be specified using the [environmental variables **PUID** and **PGID**.](https://docs.linuxserver.io/general/understanding-puid-and-pgid) + +To get the UID and GID for the current user run these commands from a terminal: + +* `id -u` -- prints UID +* `id -g` -- prints GID + +### Docker Usage Examples + +#### Using [env-based](/docs/configuration.md#env-based-configuration) configuration + +```bash +docker run -e "SPOTIFY_CLIENT_ID=yourId" -e "SPOTIFY_CLIENT_SECRET=yourSecret" -e "MALOJA_URL=http://domain.tld" -e "MALOJA_API_KEY=1234" -e "PUID=1000" -e "PGID=1000" -p 9078:9078 -v /path/on/host/config:/config foxxmd/multi-scrobbler +``` + +#### Using [file-based](/docs/configuration.md#file-based-configuration) configuration + +```bash +docker run -e "PUID=1000" -e "PGID=1000" -p 9078:9078 -v /path/on/host/config:/config foxxmd/multi-scrobbler +``` + +See the [docker-compose.yml](/docker-compose.yml) file for how to use with docker-compose. -- 2.51.2 From fc26125f80edd14f0907b35784a91b3d5921dc4a Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 20 Feb 2023 11:19:33 -0500 Subject: [PATCH 45/64] fix anchor --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index f230c04e..c495e89e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -35,7 +35,7 @@ # Configuration Overview -[**Sources** and **Clients**](/README.md#how-does-multi-scrobbler--ms--work) are configured using environmental (ENV) variables and/or json files. +[**Sources** and **Clients**](/README.md#how-does-multi-scrobbler-ms-work) are configured using environmental (ENV) variables and/or json files. **MS will parse configuration from both configuration types.** You can mix and match configurations but it is generally better to stick to one or the other. -- 2.51.2 From 27acfa948981579bc66523ee140fb5c80740740f Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 20 Feb 2023 11:31:24 -0500 Subject: [PATCH 46/64] docs: Simplify oauth config by removing unused access/refresh token Although it was nice to include the option no one is providing their own tokens from a separate flow completed elsewhere. It simplifies readability and usage in MS to remove these and always generate out own. --- config/deezer.json.example | 1 - config/spotify.json.example | 2 -- docs/configuration.md | 3 --- 3 files changed, 6 deletions(-) diff --git a/config/deezer.json.example b/config/deezer.json.example index 42aafdae..4db4f5ad 100644 --- a/config/deezer.json.example +++ b/config/deezer.json.example @@ -5,7 +5,6 @@ "data": { "clientId": "string", // deezer APPLICATION ID -- required if not providing access token "clientSecret": "string", // deezer SECRET KEY -- required if not providing access token - "accessToken": "string", // deezer access token -- required if not providing client id/secret "redirectUri": "http://localhost:9078/deezer/callback",// deezer redirect URI -- required only if not the default shown here. URI must end in "deezer/callback" "interval": 60, // optional, how long to wait before calling spotify for new tracks // ALSO see config.json.example for default properties that can be overridden here (in sourceDefaults) diff --git a/config/spotify.json.example b/config/spotify.json.example index f63508b6..8bf821a0 100644 --- a/config/spotify.json.example +++ b/config/spotify.json.example @@ -5,8 +5,6 @@ "data": { "clientId": "string", // spotify client id -- required if not providing access token "clientSecret": "string", // spotify client secret -- required if not providing access token - "accessToken": "string", // spotify access token -- required if not providing client id/secret - "refreshToken": "string", // spotify refresh token -- recommended to provide if not providing client id/secret "redirectUri": "http://localhost:9078/callback",// spotify redirect URI -- required only if not the default shown here. URI must end in "callback" "interval": 60, // optional, how long to wait before calling spotify for new tracks // ALSO see config.json.example for default properties that can be overridden here (in sourceDefaults) diff --git a/docs/configuration.md b/docs/configuration.md index c495e89e..28281615 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -179,8 +179,6 @@ Client ID/Secret. Make sure to also whitelist your redirect URI in the applicati |----------------------------|-----------|----------------------------------|----------------------------------------------------| | `SPOTIFY_CLIENT_ID` | Yes | | | | `SPOTIFY_CLIENT_SECRET` | Yes | | | -| `SPOTIFY_ACCESS_TOKEN` | No | | Must include either this token or client id/secret | -| `SPOTIFY_REFRESH_TOKEN` | No | | If using access token this is also recommended | | `SPOTIFY_REDIRECT_URI` | No | `http://localhost:9078/callback` | URI must end in `callback` | ### JSON-Based @@ -302,7 +300,6 @@ After starting multi-scrobbler with credentials in-place open the dashboard (`ht |----------------------------|-----------|-----------------------------------------|----------------------------------------------------| | `DEEZER_CLIENT_ID` | Yes | | Your **Application ID** | | `DEEZER_CLIENT_SECRET` | Yes | | Your **Secret Key** | -| `DEEZER_ACCESS_TOKEN` | No | | Must include either this token or client id/secret | | `DEEZER_REDIRECT_URI` | No | `http://localhost:9078/deezer/callback` | URI must end in `deezer/callback` | ### JSON-Based -- 2.51.2 From be9938fe95071b7ae168b1bcd5e39b168214bfa4 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 20 Feb 2023 11:32:47 -0500 Subject: [PATCH 47/64] missed some wording --- config/deezer.json.example | 4 ++-- config/spotify.json.example | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/deezer.json.example b/config/deezer.json.example index 4db4f5ad..eb890025 100644 --- a/config/deezer.json.example +++ b/config/deezer.json.example @@ -3,8 +3,8 @@ "name": "FoxxMDeezer", "clients": [], // optional, list of scrobble clients (by config name) that this source should scrobble to. Using an empty list or not including this property will make this source scrobble to all clients. "data": { - "clientId": "string", // deezer APPLICATION ID -- required if not providing access token - "clientSecret": "string", // deezer SECRET KEY -- required if not providing access token + "clientId": "string", // deezer APPLICATION ID + "clientSecret": "string", // deezer SECRET KEY "redirectUri": "http://localhost:9078/deezer/callback",// deezer redirect URI -- required only if not the default shown here. URI must end in "deezer/callback" "interval": 60, // optional, how long to wait before calling spotify for new tracks // ALSO see config.json.example for default properties that can be overridden here (in sourceDefaults) diff --git a/config/spotify.json.example b/config/spotify.json.example index 8bf821a0..bb9e1b93 100644 --- a/config/spotify.json.example +++ b/config/spotify.json.example @@ -3,8 +3,8 @@ "name": "default", // optional, friendly name for logs "clients": [], // optional, list of scrobble clients (by config name) that this source should scrobble to. Using an empty list or not including this property will make this source scrobble to all clients. "data": { - "clientId": "string", // spotify client id -- required if not providing access token - "clientSecret": "string", // spotify client secret -- required if not providing access token + "clientId": "string", // spotify client id + "clientSecret": "string", // spotify client secret "redirectUri": "http://localhost:9078/callback",// spotify redirect URI -- required only if not the default shown here. URI must end in "callback" "interval": 60, // optional, how long to wait before calling spotify for new tracks // ALSO see config.json.example for default properties that can be overridden here (in sourceDefaults) -- 2.51.2 From 70a1181ea1983e6c2cef8e8bbf208add5902d65b Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 11:00:56 -0500 Subject: [PATCH 48/64] chore: Add missing passport-deezer typings --- src/common/infrastructure/typings/passport-deezer.d.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/common/infrastructure/typings/passport-deezer.d.ts diff --git a/src/common/infrastructure/typings/passport-deezer.d.ts b/src/common/infrastructure/typings/passport-deezer.d.ts new file mode 100644 index 00000000..21c498cd --- /dev/null +++ b/src/common/infrastructure/typings/passport-deezer.d.ts @@ -0,0 +1,8 @@ + +declare module 'passport-deezer' { + //import {Strategy as Oauth2Strategy} from 'passport-oauth2'; + import {Strategy as PassportStrategy} from "passport"; + export class Strategy extends PassportStrategy { + constructor(options: any, verify: any); + } +} -- 2.51.2 From 06c0b64ee5209249a9afab0df73fb66d033862e1 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 11:01:28 -0500 Subject: [PATCH 49/64] fix: Correct plex data types --- .../infrastructure/config/source/plex.ts | 6 +-- src/sources/PlexSource.ts | 54 ++++++++----------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/common/infrastructure/config/source/plex.ts b/src/common/infrastructure/config/source/plex.ts index 1817f37e..3ad04e80 100644 --- a/src/common/infrastructure/config/source/plex.ts +++ b/src/common/infrastructure/config/source/plex.ts @@ -7,19 +7,19 @@ export interface PlexSourceConfig extends CommonSourceConfig { * * If none are provided tracks from all users will be scrobbled * */ - user?: string + user?: string | string[] /** * optional list of libraries to scrobble tracks from * * If none are provided tracks from all libraries will be scrobbled * */ - libraries?: string + libraries?: string | string[] /** * optional list of servers to scrobble tracks from * * If none are provided tracks from all servers will be scrobbled * */ - servers?: string + servers?: string | string[] } } diff --git a/src/sources/PlexSource.ts b/src/sources/PlexSource.ts index 48b692b4..e4d43ed6 100644 --- a/src/sources/PlexSource.ts +++ b/src/sources/PlexSource.ts @@ -7,53 +7,41 @@ import {PlexSourceConfig} from "../common/infrastructure/config/source/plex.js"; import {InternalConfig, PlayObject, SourceType} from "../common/infrastructure/Atomic.js"; export default class PlexSource extends AbstractSource { - users; - libraries; - servers; + users: string[]; + libraries: string[]; + servers: string[]; declare config: PlexSourceConfig; constructor(name: any, config: PlexSourceConfig, internal: InternalConfig, type: SourceType = 'plex') { super(type, name, config, internal); - const {data: {user, libraries, servers} = {}} = config + const {data: {user = [], libraries = [], servers = []} = {}} = config - if (user === undefined || user === null) { - this.users = undefined; + if (!Array.isArray(user)) { + this.users = [user]; } else { - if (!Array.isArray(user)) { - this.users = [user]; - } else { - this.users = user; - } - this.users = this.users.map((x: any) => x.toLocaleLowerCase()) + this.users = user; } + this.users = this.users.map((x: any) => x.toLocaleLowerCase()) - if (libraries === undefined || libraries === null) { - this.libraries = undefined; + if (!Array.isArray(libraries)) { + this.libraries = [libraries]; } else { - if (!Array.isArray(libraries)) { - this.libraries = [libraries]; - } else { - this.libraries = libraries; - } - this.libraries = this.libraries.map((x: any) => x.toLocaleLowerCase()) + this.libraries = libraries; } + this.libraries = this.libraries.map((x: any) => x.toLocaleLowerCase()) - if (servers === undefined || servers === null) { - this.servers = undefined; + if (!Array.isArray(servers)) { + this.servers = [servers]; } else { - if (!Array.isArray(servers)) { - this.servers = [servers]; - } else { - this.servers = servers; - } - this.servers = this.servers.map((x: any) => x.toLocaleLowerCase()) + this.servers = servers; } + this.servers = this.servers.map((x: any) => x.toLocaleLowerCase()) - if (user === undefined && libraries === undefined && servers === undefined) { + if (this.users.length === 0 && this.libraries.length === 0 && this.servers.length === 0) { this.logger.warn('Initializing, but with no filters! All tracks from all users on all servers and libraries will be scrobbled.'); } else { - this.logger.info(`Initializing with the following filters => Users: ${this.users === undefined ? 'N/A' : this.users.join(', ')} | Libraries: ${this.libraries === undefined ? 'N/A' : this.libraries.join(', ')} | Servers: ${this.servers === undefined ? 'N/A' : this.servers.join(', ')}`); + this.logger.info(`Initializing with the following filters => Users: ${this.users.length === 0 ? 'N/A' : this.users.join(', ')} | Libraries: ${this.libraries.length === 0 ? 'N/A' : this.libraries.join(', ')} | Servers: ${this.servers.length === 0 ? 'N/A' : this.servers.join(', ')}`); } this.initialized = true; } @@ -114,7 +102,7 @@ export default class PlexSource extends AbstractSource { const hint = this.type === 'tautulli' ? ' (Check notification agent json data configuration)' : ''; - if (this.users !== undefined) { + if (this.users.length !== 0) { if (user === undefined) { this.logger.warn(`Config defined users but payload contained no user info${hint}`); } else if (!this.users.includes(user.toLocaleLowerCase())) { @@ -142,7 +130,7 @@ export default class PlexSource extends AbstractSource { return false; } - if (this.libraries !== undefined) { + if (this.libraries.length !== 0) { if (library === undefined) { this.logger.warn(`Config defined libraries but payload contained no library info${hint}`); } else if (!this.libraries.includes(library.toLocaleLowerCase())) { @@ -154,7 +142,7 @@ export default class PlexSource extends AbstractSource { } } - if (this.servers !== undefined) { + if (this.servers.length !== 0) { if (server === undefined) { this.logger.warn(`Config defined server but payload contained no server info${hint}`); } else if (!this.servers.includes(server.toLocaleLowerCase())) { -- 2.51.2 From c9bac6f7d23e6c9825891b45f8187fa302ec1598 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 11:02:30 -0500 Subject: [PATCH 50/64] fix: Use separate interface for maloja config data with inherited properties --- .../infrastructure/config/client/maloja.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/common/infrastructure/config/client/maloja.ts b/src/common/infrastructure/config/client/maloja.ts index d0f267ea..e9e060ff 100644 --- a/src/common/infrastructure/config/client/maloja.ts +++ b/src/common/infrastructure/config/client/maloja.ts @@ -1,17 +1,19 @@ import {CommonClientConfig, CommonClientData} from "./index.js"; +import {RequestRetryOptions} from "../common.js"; -export interface MalojaClientConfig extends CommonClientConfig { - data: CommonClientData & { - /** - * URL for maloja server - * */ - url: string - /** - * API Key for Maloja server - * */ - apiKey: string +export interface MalojaClientData extends RequestRetryOptions, CommonClientData { + /** + * URL for maloja server + * */ + url: string + /** + * API Key for Maloja server + * */ + apiKey: string +} - } +export interface MalojaClientConfig extends CommonClientConfig { + data: MalojaClientData } export interface MalojaClientAIOConfig extends MalojaClientConfig { -- 2.51.2 From a3fd30c427a328d9c84effb5d74523d9d61e6573 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 11:05:33 -0500 Subject: [PATCH 51/64] fix: Update project TS and node configuration in order to correctly generate json schema * Output to commonjs using TS (remove type:module from package.json) * Update tsconfig config * Downgrade formidable to fix ES module import error * Remove js assertions and use separate json schema for aio client/source validation to keep MS log output cleaner --- package-lock.json | 75 +-- package.json | 13 +- src/clients/ScrobbleClients.ts | 4 +- src/common/index.ts | 4 +- src/common/infrastructure/config/aioConfig.ts | 10 + src/common/schema/aio-client.json | 233 +++++++ src/common/schema/aio-source.json | 624 ++++++++++++++++++ src/common/schema/aio.json | 547 +++++++++------ src/common/schema/client.json | 111 ++-- src/common/schema/source.json | 250 ++++--- src/sources/ScrobbleSources.ts | 8 +- src/utils.ts | 6 +- tsconfig.json | 6 +- 13 files changed, 1488 insertions(+), 403 deletions(-) create mode 100644 src/common/schema/aio-client.json create mode 100644 src/common/schema/aio-source.json diff --git a/package-lock.json b/package-lock.json index fd8393ff..052e7530 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,10 +19,10 @@ "es6-error": "^4.1.1", "express": "^4.17.1", "express-session": "^1.17.2", - "formidable": "^3.2.4", + "formidable": "^2.1", "json5": "^2.2.3", "lastfm-node-client": "^2.2.0", - "passport": "^0.5.0", + "passport": "^0.6.0", "passport-deezer": "^0.2.0", "pony-cause": "^1.1.1", "safe-stable-stringify": "^1.1.1", @@ -43,7 +43,7 @@ "@types/express-session": "^1.17.4", "@types/formidable": "^2.0.5", "@types/node": "^18.0.0", - "@types/passport": "^1.0.7", + "@types/passport": "^1.0.12", "@types/spotify-web-api-node": "^5.0.7", "@types/superagent": "^4.1.16", "@types/triple-beam": "^1.3.2", @@ -266,14 +266,14 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", - "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" + "version": "18.14.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.0.tgz", + "integrity": "sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==" }, "node_modules/@types/passport": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.11.tgz", - "integrity": "sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz", + "integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==", "dev": true, "dependencies": { "@types/express": "*" @@ -749,9 +749,9 @@ } }, "node_modules/dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dependencies": { "asap": "^2.0.0", "wrappy": "1" @@ -1041,13 +1041,14 @@ } }, "node_modules/formidable": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", - "integrity": "sha512-GRGDJTWAZ3H+umZbF2bKcqjsTov25zgon1St05ziKdiSw3kxvI+meMJrXx3ylRmuSADOpviSakBuS4yvGCGnSg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", "dependencies": { - "dezalgo": "1.0.3", - "hexoid": "1.0.0", - "once": "1.4.0" + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" @@ -1488,12 +1489,13 @@ } }, "node_modules/passport": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.3.tgz", - "integrity": "sha512-gGc+70h4gGdBWNsR3FuV3byLDY6KBTJAIExGFXTpQaYfbbcHCBlRRKx7RBQSpqEqc5Hh2qVzRs7ssvSfOpkUEA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", "dependencies": { "passport-strategy": "1.x.x", - "pause": "0.0.1" + "pause": "0.0.1", + "utils-merge": "^1.0.1" }, "engines": { "node": ">= 0.4.0" @@ -1958,29 +1960,6 @@ } } }, - "node_modules/superagent/node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/superagent/node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, "node_modules/superagent/node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -2426,9 +2405,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.0.tgz", + "integrity": "sha512-dwqOPg5trmrre9+v8SUo2q/hAwyKoVfu8OC1xPHKJGNdxAvPl4sKxL4vBnh3bQz/ZvvGAFeA5H3ou2kcOY8sQQ==", "dev": true, "dependencies": { "cliui": "^8.0.1", diff --git a/package.json b/package.json index 79ff285d..913703b3 100644 --- a/package.json +++ b/package.json @@ -3,15 +3,16 @@ "version": "0.4.0", "description": "scrobble plays from multiple sources to multiple clients", "main": "src/index.js", - "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "tsc", "start": "node src/index.js", - "schema": "npm run -s schema-aio & npm run -s schema-sources & npm run -s schema-client", + "schema": "npm run -s schema-aio & npm run -s schema-sources & npm run -s schema-client & npm run -s schema-aiosource & npm run -s schema-aioclient", "schema-client": "typescript-json-schema tsconfig.json ClientConfig --out src/common/schema/client.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", "schema-sources": "typescript-json-schema tsconfig.json SourceConfig --out src/common/schema/source.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", - "schema-aio": "typescript-json-schema tsconfig.json AIOConfig --out src/common/schema/aio.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage" + "schema-aio": "typescript-json-schema tsconfig.json AIOConfig --out src/common/schema/aio.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", + "schema-aiosource": "typescript-json-schema tsconfig.json AIOSourceConfig --out src/common/schema/aio-source.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", + "schema-aioclient": "typescript-json-schema tsconfig.json AIOClientConfig --out src/common/schema/aio-client.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage" }, "exports": { ".": { @@ -44,10 +45,10 @@ "es6-error": "^4.1.1", "express": "^4.17.1", "express-session": "^1.17.2", - "formidable": "^3.2.4", + "formidable": "^2.1", "json5": "^2.2.3", "lastfm-node-client": "^2.2.0", - "passport": "^0.5.0", + "passport": "^0.6.0", "passport-deezer": "^0.2.0", "pony-cause": "^1.1.1", "safe-stable-stringify": "^1.1.1", @@ -68,7 +69,7 @@ "@types/express-session": "^1.17.4", "@types/formidable": "^2.0.5", "@types/node": "^18.0.0", - "@types/passport": "^1.0.7", + "@types/passport": "^1.0.12", "@types/spotify-web-api-node": "^5.0.7", "@types/superagent": "^4.1.16", "@types/triple-beam": "^1.3.2", diff --git a/src/clients/ScrobbleClients.ts b/src/clients/ScrobbleClients.ts index f9841dce..a3f6a1e6 100644 --- a/src/clients/ScrobbleClients.ts +++ b/src/clients/ScrobbleClients.ts @@ -10,8 +10,8 @@ import MalojaScrobbler from "./MalojaScrobbler.js"; import LastfmScrobbler from "./LastfmScrobbler.js"; import {clientTypes, ConfigMeta, PlayObject} from "../common/infrastructure/Atomic.js"; import {AIOConfig} from "../common/infrastructure/config/aioConfig.js"; -import * as aioSchema from '../common/schema/aio.json' assert {type: "json"}; -import * as clientSchema from '../common/schema/client.json' assert {type: "json"}; +import * as aioSchema from '../common/schema/aio-client.json'; +import * as clientSchema from '../common/schema/client.json'; import {ClientAIOConfig, ClientConfig} from "../common/infrastructure/config/client/clients.js"; import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja.js"; import {LastfmClientConfig} from "../common/infrastructure/config/client/lastfm.js"; diff --git a/src/common/index.ts b/src/common/index.ts index fa45b338..a0025a74 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,8 +1,8 @@ import * as path from 'path'; import {fileURLToPath} from "url"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +//const __filename = fileURLToPath(import.meta.url); +//const __dirname = path.dirname(__filename); export const projectDir = path.resolve(__dirname, '../../'); export const configDir: string = path.resolve(projectDir, './config'); diff --git a/src/common/infrastructure/config/aioConfig.ts b/src/common/infrastructure/config/aioConfig.ts index 665ab70c..a8a47efe 100644 --- a/src/common/infrastructure/config/aioConfig.ts +++ b/src/common/infrastructure/config/aioConfig.ts @@ -9,3 +9,13 @@ export interface AIOConfig { sources?: SourceAIOConfig[] clients?: ClientAIOConfig[] } + +export interface AIOClientConfig { + clientDefaults?: RequestRetryOptions + clients?: ClientAIOConfig[] +} + +export interface AIOSourceConfig { + sourceDefaults?: SourceRetryOptions + sources?: SourceAIOConfig[] +} diff --git a/src/common/schema/aio-client.json b/src/common/schema/aio-client.json new file mode 100644 index 00000000..57da8072 --- /dev/null +++ b/src/common/schema/aio-client.json @@ -0,0 +1,233 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "ClientAIOConfig": { + "anyOf": [ + { + "$ref": "#/definitions/LastfmClientAIOConfig" + }, + { + "$ref": "#/definitions/MalojaClientAIOConfig" + } + ] + }, + "CommonClientData": { + "properties": { + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a client can make before error is thrown.", + "type": "number" + }, + "options": { + "properties": { + "checkExistingScrobbles": { + "type": "boolean" + }, + "refreshEnabled": { + "type": "boolean" + }, + "verbose": { + "properties": { + "match": { + "$ref": "#/definitions/MatchLoggingOptions" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying).", + "type": "number" + } + }, + "type": "object" + }, + "LastfmClientAIOConfig": { + "properties": { + "configureAs": { + "enum": [ + "client", + "source" + ], + "type": "string" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonClientData" + }, + { + "$ref": "#/definitions/LastfmData" + } + ] + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + }, + "type": { + "enum": [ + "lastfm" + ], + "type": "string" + } + }, + "required": [ + "data", + "name", + "type" + ], + "type": "object" + }, + "LastfmData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" + } + }, + "required": [ + "apiKey", + "secret" + ], + "type": "object" + }, + "MalojaClientAIOConfig": { + "properties": { + "data": { + "$ref": "#/definitions/MalojaClientData" + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + }, + "type": { + "enum": [ + "maloja" + ], + "type": "string" + } + }, + "required": [ + "data", + "name", + "type" + ], + "type": "object" + }, + "MalojaClientData": { + "properties": { + "apiKey": { + "description": "API Key for Maloja server", + "type": "string" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "properties": { + "checkExistingScrobbles": { + "type": "boolean" + }, + "refreshEnabled": { + "type": "boolean" + }, + "verbose": { + "properties": { + "match": { + "$ref": "#/definitions/MatchLoggingOptions" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "url": { + "description": "URL for maloja server", + "type": "string" + } + }, + "required": [ + "apiKey", + "url" + ], + "type": "object" + }, + "MatchLoggingOptions": { + "properties": { + "confidenceBreakdown": { + "type": "boolean" + }, + "onMatch": { + "type": "boolean" + }, + "onNoMatch": { + "type": "boolean" + } + }, + "type": "object" + }, + "RequestRetryOptions": { + "properties": { + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + } + }, + "properties": { + "clientDefaults": { + "$ref": "#/definitions/RequestRetryOptions" + }, + "clients": { + "items": { + "$ref": "#/definitions/ClientAIOConfig" + }, + "type": "array" + } + }, + "type": "object" +} + diff --git a/src/common/schema/aio-source.json b/src/common/schema/aio-source.json new file mode 100644 index 00000000..669cd243 --- /dev/null +++ b/src/common/schema/aio-source.json @@ -0,0 +1,624 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "CommonSourceData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "DeezerData": { + "properties": { + "clientId": { + "description": "deezer client id", + "type": "string" + }, + "clientSecret": { + "description": "deezer client secret", + "type": "string" + }, + "interval": { + "default": 60, + "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "type": "number" + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "redirectUri": { + "default": "http://localhost:9078/deezer/callback", + "description": "deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "required": [ + "clientId", + "clientSecret", + "redirectUri" + ], + "type": "object" + }, + "DeezerSourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/DeezerData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "deezer" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "JellyData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "servers": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" + }, + "users": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" + } + }, + "type": "object" + }, + "JellySourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/JellyData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "jellyfin" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "LastFmSouceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "configureAs": { + "enum": [ + "source" + ], + "type": "string" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "$ref": "#/definitions/LastfmData" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "lastfm" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "LastfmData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" + } + }, + "required": [ + "apiKey", + "secret" + ], + "type": "object" + }, + "PlexSourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "properties": { + "libraries": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" + }, + "servers": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" + }, + "user": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" + } + }, + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "plex" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "Record": { + "type": "object" + }, + "SourceAIOConfig": { + "anyOf": [ + { + "$ref": "#/definitions/SpotifySourceAIOConfig" + }, + { + "$ref": "#/definitions/PlexSourceAIOConfig" + }, + { + "$ref": "#/definitions/TautulliSourceAIOConfig" + }, + { + "$ref": "#/definitions/DeezerSourceAIOConfig" + }, + { + "$ref": "#/definitions/SubsonicSourceAIOConfig" + }, + { + "$ref": "#/definitions/JellySourceAIOConfig" + }, + { + "$ref": "#/definitions/LastFmSouceAIOConfig" + } + ] + }, + "SourceRetryOptions": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "SpotifySourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/SpotifySourceData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "spotify" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "SpotifySourceData": { + "properties": { + "clientId": { + "description": "spotify client id", + "type": "string" + }, + "clientSecret": { + "description": "spotify client secret", + "type": "string" + }, + "interval": { + "default": 60, + "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "type": "number" + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "redirectUri": { + "default": "http://localhost:9078/callback", + "description": "spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "required": [ + "clientId", + "clientSecret", + "redirectUri" + ], + "type": "object" + }, + "SubsonicData": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "password": { + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "url": { + "description": "URL of the subsonic media server to query", + "type": "string" + }, + "user": { + "type": "string" + } + }, + "required": [ + "password", + "url", + "user" + ], + "type": "object" + }, + "SubsonicSourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/definitions/SubsonicData" + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "subsonic" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + }, + "TautulliSourceAIOConfig": { + "properties": { + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" + }, + "data": { + "allOf": [ + { + "$ref": "#/definitions/CommonSourceData" + }, + { + "properties": { + "libraries": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" + }, + "servers": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" + }, + "user": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" + } + }, + "type": "object" + } + ] + }, + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "tautulli" + ], + "type": "string" + } + }, + "required": [ + "data", + "type" + ], + "type": "object" + } + }, + "properties": { + "sourceDefaults": { + "$ref": "#/definitions/SourceRetryOptions" + }, + "sources": { + "items": { + "$ref": "#/definitions/SourceAIOConfig" + }, + "type": "array" + } + }, + "type": "object" +} + diff --git a/src/common/schema/aio.json b/src/common/schema/aio.json index 0c5ff1f1..d5b8cf65 100644 --- a/src/common/schema/aio.json +++ b/src/common/schema/aio.json @@ -1,20 +1,17 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "Record": { - "type": "object" - }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/clients\",{assert:{\"resolution-mode\":\"import\"}}).ClientAIOConfig": { + "ClientAIOConfig": { "anyOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmClientAIOConfig" + "$ref": "#/definitions/LastfmClientAIOConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/maloja\",{assert:{\"resolution-mode\":\"import\"}}).MalojaClientAIOConfig" + "$ref": "#/definitions/MalojaClientAIOConfig" } ] }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData": { + "CommonClientData": { "properties": { "maxRequestRetries": { "default": 1, @@ -32,7 +29,7 @@ "verbose": { "properties": { "match": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).MatchLoggingOptions" + "$ref": "#/definitions/MatchLoggingOptions" } }, "type": "object" @@ -48,144 +45,21 @@ }, "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).MatchLoggingOptions": { - "properties": { - "confidenceBreakdown": { - "type": "boolean" - }, - "onMatch": { - "type": "boolean" - }, - "onNoMatch": { - "type": "boolean" - } - }, - "type": "object" - }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmClientAIOConfig": { + "CommonSourceData": { "properties": { - "configureAs": { - "enum": [ - "client", - "source" - ], - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData" - }, - { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData" - } - ] - }, - "name": { - "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", - "type": "string" - }, - "type": { - "enum": [ - "lastfm" - ], - "type": "string" - } - }, - "required": [ - "data", - "name", - "type" - ], - "type": "object" - }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData": { - "properties": { - "apiKey": { - "description": "API Key generated from Last.fm account", - "type": "string" - }, - "maxRequestRetries": { - "default": 1, - "description": "default # of http request retries a source can make before error is thrown", - "type": "number" - }, - "redirectUri": { - "default": "http://localhost:9078/lastfm/callback", - "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", - "type": "string" - }, - "retryMultiplier": { - "default": 1.5, - "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", "type": "number" }, - "secret": { - "description": "Secret generated from Last.fm account", - "type": "string" - }, - "session": { - "description": "Optional session id returned from a completed auth flow", - "type": "string" - } - }, - "required": [ - "apiKey", - "secret" - ], - "type": "object" - }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/maloja\",{assert:{\"resolution-mode\":\"import\"}}).MalojaClientAIOConfig": { - "properties": { - "data": { - "allOf": [ - { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData" - }, - { - "properties": { - "apiKey": { - "description": "API Key for Maloja server", - "type": "string" - }, - "url": { - "description": "URL for maloja server", - "type": "string" - } - }, - "required": [ - "apiKey", - "url" - ], - "type": "object" - } - ] - }, - "name": { - "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", - "type": "string" - }, - "type": { - "enum": [ - "maloja" - ], - "type": "string" - } - }, - "required": [ - "data", - "name", - "type" - ], - "type": "object" - }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/common\",{assert:{\"resolution-mode\":\"import\"}}).RequestRetryOptions": { - "properties": { "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", "type": "number" }, + "options": { + "$ref": "#/definitions/Record" + }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", @@ -194,7 +68,7 @@ }, "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerData": { + "DeezerData": { "properties": { "clientId": { "description": "deezer client id", @@ -240,7 +114,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerSourceAIOConfig": { + "DeezerSourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -250,7 +124,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerData" + "$ref": "#/definitions/DeezerData" }, "name": { "description": "Unique identifier for this source.", @@ -269,7 +143,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData": { + "JellyData": { "properties": { "maxPollRetries": { "default": 0, @@ -288,31 +162,68 @@ "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", "type": "number" + }, + "servers": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" + }, + "users": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" } }, "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).SourceRetryOptions": { + "JellySourceAIOConfig": { "properties": { - "maxPollRetries": { - "default": 0, - "description": "default # of automatic polling restarts on error", - "type": "number" + "clients": { + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "items": { + "type": "string" + }, + "type": "array" }, - "maxRequestRetries": { - "default": 1, - "description": "default # of http request retries a source can make before error is thrown", - "type": "number" + "data": { + "$ref": "#/definitions/JellyData" }, - "retryMultiplier": { - "default": 1.5, - "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", - "type": "number" + "name": { + "description": "Unique identifier for this source.", + "type": "string" + }, + "type": { + "enum": [ + "jellyfin" + ], + "type": "string" } }, + "required": [ + "data", + "type" + ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/jellyfin\",{assert:{\"resolution-mode\":\"import\"}}).JellySourceAIOConfig": { + "LastFmSouceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -321,23 +232,19 @@ }, "type": "array" }, + "configureAs": { + "enum": [ + "source" + ], + "type": "string" + }, "data": { "allOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" + "$ref": "#/definitions/CommonSourceData" }, { - "properties": { - "servers": { - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", - "type": "string" - }, - "user": { - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", - "type": "string" - } - }, - "type": "object" + "$ref": "#/definitions/LastfmData" } ] }, @@ -347,7 +254,7 @@ }, "type": { "enum": [ - "jellyfin" + "lastfm" ], "type": "string" } @@ -358,17 +265,11 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastFmSouceAIOConfig": { + "LastfmClientAIOConfig": { "properties": { - "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", - "items": { - "type": "string" - }, - "type": "array" - }, "configureAs": { "enum": [ + "client", "source" ], "type": "string" @@ -376,15 +277,15 @@ "data": { "allOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" + "$ref": "#/definitions/CommonClientData" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData" + "$ref": "#/definitions/LastfmData" } ] }, "name": { - "description": "Unique identifier for this source.", + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", "type": "string" }, "type": { @@ -396,11 +297,131 @@ }, "required": [ "data", + "name", "type" ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/plex\",{assert:{\"resolution-mode\":\"import\"}}).PlexSourceAIOConfig": { + "LastfmData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" + } + }, + "required": [ + "apiKey", + "secret" + ], + "type": "object" + }, + "MalojaClientAIOConfig": { + "properties": { + "data": { + "$ref": "#/definitions/MalojaClientData" + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + }, + "type": { + "enum": [ + "maloja" + ], + "type": "string" + } + }, + "required": [ + "data", + "name", + "type" + ], + "type": "object" + }, + "MalojaClientData": { + "properties": { + "apiKey": { + "description": "API Key for Maloja server", + "type": "string" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "properties": { + "checkExistingScrobbles": { + "type": "boolean" + }, + "refreshEnabled": { + "type": "boolean" + }, + "verbose": { + "properties": { + "match": { + "$ref": "#/definitions/MatchLoggingOptions" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "url": { + "description": "URL for maloja server", + "type": "string" + } + }, + "required": [ + "apiKey", + "url" + ], + "type": "object" + }, + "MatchLoggingOptions": { + "properties": { + "confidenceBreakdown": { + "type": "boolean" + }, + "onMatch": { + "type": "boolean" + }, + "onNoMatch": { + "type": "boolean" + } + }, + "type": "object" + }, + "PlexSourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -412,21 +433,51 @@ "data": { "allOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" + "$ref": "#/definitions/CommonSourceData" }, { "properties": { "libraries": { - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" }, "servers": { - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" }, "user": { - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" } }, "type": "object" @@ -450,32 +501,70 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/sources\",{assert:{\"resolution-mode\":\"import\"}}).SourceAIOConfig": { + "Record": { + "type": "object" + }, + "RequestRetryOptions": { + "properties": { + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "SourceAIOConfig": { "anyOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceAIOConfig" + "$ref": "#/definitions/SpotifySourceAIOConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/plex\",{assert:{\"resolution-mode\":\"import\"}}).PlexSourceAIOConfig" + "$ref": "#/definitions/PlexSourceAIOConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/tautulli\",{assert:{\"resolution-mode\":\"import\"}}).TautulliSourceAIOConfig" + "$ref": "#/definitions/TautulliSourceAIOConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerSourceAIOConfig" + "$ref": "#/definitions/DeezerSourceAIOConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicSourceAIOConfig" + "$ref": "#/definitions/SubsonicSourceAIOConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/jellyfin\",{assert:{\"resolution-mode\":\"import\"}}).JellySourceAIOConfig" + "$ref": "#/definitions/JellySourceAIOConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastFmSouceAIOConfig" + "$ref": "#/definitions/LastFmSouceAIOConfig" } ] }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceAIOConfig": { + "SourceRetryOptions": { + "properties": { + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + } + }, + "type": "object" + }, + "SpotifySourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -485,7 +574,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceData" + "$ref": "#/definitions/SpotifySourceData" }, "name": { "description": "Unique identifier for this source.", @@ -504,7 +593,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceData": { + "SpotifySourceData": { "properties": { "clientId": { "description": "spotify client id", @@ -550,7 +639,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicData": { + "SubsonicData": { "properties": { "maxPollRetries": { "default": 0, @@ -588,7 +677,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicSourceAIOConfig": { + "SubsonicSourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -598,7 +687,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicData" + "$ref": "#/definitions/SubsonicData" }, "name": { "description": "Unique identifier for this source.", @@ -617,7 +706,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/tautulli\",{assert:{\"resolution-mode\":\"import\"}}).TautulliSourceAIOConfig": { + "TautulliSourceAIOConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -629,21 +718,51 @@ "data": { "allOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" + "$ref": "#/definitions/CommonSourceData" }, { "properties": { "libraries": { - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" }, "servers": { - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" }, "user": { - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" } }, "type": "object" @@ -670,20 +789,20 @@ }, "properties": { "clientDefaults": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/common\",{assert:{\"resolution-mode\":\"import\"}}).RequestRetryOptions" + "$ref": "#/definitions/RequestRetryOptions" }, "clients": { "items": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/clients\",{assert:{\"resolution-mode\":\"import\"}}).ClientAIOConfig" + "$ref": "#/definitions/ClientAIOConfig" }, "type": "array" }, "sourceDefaults": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).SourceRetryOptions" + "$ref": "#/definitions/SourceRetryOptions" }, "sources": { "items": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/sources\",{assert:{\"resolution-mode\":\"import\"}}).SourceAIOConfig" + "$ref": "#/definitions/SourceAIOConfig" }, "type": "array" } diff --git a/src/common/schema/client.json b/src/common/schema/client.json index a4c35aaf..c486c828 100644 --- a/src/common/schema/client.json +++ b/src/common/schema/client.json @@ -2,14 +2,14 @@ "$schema": "http://json-schema.org/draft-07/schema#", "anyOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmClientConfig" + "$ref": "#/definitions/LastfmClientConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/maloja\",{assert:{\"resolution-mode\":\"import\"}}).MalojaClientConfig" + "$ref": "#/definitions/MalojaClientConfig" } ], "definitions": { - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData": { + "CommonClientData": { "properties": { "maxRequestRetries": { "default": 1, @@ -27,7 +27,7 @@ "verbose": { "properties": { "match": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).MatchLoggingOptions" + "$ref": "#/definitions/MatchLoggingOptions" } }, "type": "object" @@ -43,21 +43,7 @@ }, "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).MatchLoggingOptions": { - "properties": { - "confidenceBreakdown": { - "type": "boolean" - }, - "onMatch": { - "type": "boolean" - }, - "onNoMatch": { - "type": "boolean" - } - }, - "type": "object" - }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmClientConfig": { + "LastfmClientConfig": { "properties": { "configureAs": { "enum": [ @@ -69,10 +55,10 @@ "data": { "allOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData" + "$ref": "#/definitions/CommonClientData" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData" + "$ref": "#/definitions/LastfmData" } ] }, @@ -87,7 +73,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData": { + "LastfmData": { "properties": { "apiKey": { "description": "API Key generated from Last.fm account", @@ -123,42 +109,81 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/maloja\",{assert:{\"resolution-mode\":\"import\"}}).MalojaClientConfig": { + "MalojaClientConfig": { "properties": { "data": { - "allOf": [ - { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonClientData" + "$ref": "#/definitions/MalojaClientData" + }, + "name": { + "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "type": "string" + } + }, + "required": [ + "data", + "name" + ], + "type": "object" + }, + "MalojaClientData": { + "properties": { + "apiKey": { + "description": "API Key for Maloja server", + "type": "string" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "options": { + "properties": { + "checkExistingScrobbles": { + "type": "boolean" }, - { + "refreshEnabled": { + "type": "boolean" + }, + "verbose": { "properties": { - "apiKey": { - "description": "API Key for Maloja server", - "type": "string" - }, - "url": { - "description": "URL for maloja server", - "type": "string" + "match": { + "$ref": "#/definitions/MatchLoggingOptions" } }, - "required": [ - "apiKey", - "url" - ], "type": "object" } - ] + }, + "type": "object" }, - "name": { - "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "url": { + "description": "URL for maloja server", "type": "string" } }, "required": [ - "data", - "name" + "apiKey", + "url" ], "type": "object" + }, + "MatchLoggingOptions": { + "properties": { + "confidenceBreakdown": { + "type": "boolean" + }, + "onMatch": { + "type": "boolean" + }, + "onNoMatch": { + "type": "boolean" + } + }, + "type": "object" } } } diff --git a/src/common/schema/source.json b/src/common/schema/source.json index d83ace20..230c9bbd 100644 --- a/src/common/schema/source.json +++ b/src/common/schema/source.json @@ -2,68 +2,52 @@ "$schema": "http://json-schema.org/draft-07/schema#", "anyOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceConfig" + "$ref": "#/definitions/SpotifySourceConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/plex\",{assert:{\"resolution-mode\":\"import\"}}).PlexSourceConfig" + "$ref": "#/definitions/PlexSourceConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/tautulli\",{assert:{\"resolution-mode\":\"import\"}}).TautulliSourceConfig" + "$ref": "#/definitions/TautulliSourceConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerSourceConfig" + "$ref": "#/definitions/DeezerSourceConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubSonicSourceConfig" + "$ref": "#/definitions/SubSonicSourceConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/jellyfin\",{assert:{\"resolution-mode\":\"import\"}}).JellySourceConfig" + "$ref": "#/definitions/JellySourceConfig" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmSourceConfig" + "$ref": "#/definitions/LastfmSourceConfig" } ], "definitions": { - "Record": { - "type": "object" - }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData": { + "CommonSourceData": { "properties": { - "apiKey": { - "description": "API Key generated from Last.fm account", - "type": "string" + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", "type": "number" }, - "redirectUri": { - "default": "http://localhost:9078/lastfm/callback", - "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", - "type": "string" + "options": { + "$ref": "#/definitions/Record" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", "type": "number" - }, - "secret": { - "description": "Secret generated from Last.fm account", - "type": "string" - }, - "session": { - "description": "Optional session id returned from a completed auth flow", - "type": "string" } }, - "required": [ - "apiKey", - "secret" - ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerData": { + "DeezerData": { "properties": { "clientId": { "description": "deezer client id", @@ -109,7 +93,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerSourceConfig": { + "DeezerSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -119,7 +103,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/deezer\",{assert:{\"resolution-mode\":\"import\"}}).DeezerData" + "$ref": "#/definitions/DeezerData" }, "name": { "description": "Unique identifier for this source.", @@ -131,7 +115,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData": { + "JellyData": { "properties": { "maxPollRetries": { "default": 0, @@ -150,11 +134,39 @@ "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", "type": "number" + }, + "servers": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" + }, + "users": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" } }, "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/jellyfin\",{assert:{\"resolution-mode\":\"import\"}}).JellySourceConfig": { + "JellySourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -164,24 +176,7 @@ "type": "array" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" - }, - { - "properties": { - "servers": { - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", - "type": "string" - }, - "user": { - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", - "type": "string" - } - }, - "type": "object" - } - ] + "$ref": "#/definitions/JellyData" }, "name": { "description": "Unique identifier for this source.", @@ -193,7 +188,43 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmSourceConfig": { + "LastfmData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "type": "string" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "type": "number" + }, + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "type": "number" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" + } + }, + "required": [ + "apiKey", + "secret" + ], + "type": "object" + }, + "LastfmSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -211,10 +242,10 @@ "data": { "allOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" + "$ref": "#/definitions/CommonSourceData" }, { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/client/lastfm\",{assert:{\"resolution-mode\":\"import\"}}).LastfmData" + "$ref": "#/definitions/LastfmData" } ] }, @@ -228,7 +259,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/plex\",{assert:{\"resolution-mode\":\"import\"}}).PlexSourceConfig": { + "PlexSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -240,21 +271,51 @@ "data": { "allOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" + "$ref": "#/definitions/CommonSourceData" }, { "properties": { "libraries": { - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" }, "servers": { - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" }, "user": { - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" } }, "type": "object" @@ -271,7 +332,10 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceConfig": { + "Record": { + "type": "object" + }, + "SpotifySourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -281,7 +345,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceData" + "$ref": "#/definitions/SpotifySourceData" }, "name": { "description": "Unique identifier for this source.", @@ -293,7 +357,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/spotify\",{assert:{\"resolution-mode\":\"import\"}}).SpotifySourceData": { + "SpotifySourceData": { "properties": { "clientId": { "description": "spotify client id", @@ -339,7 +403,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubSonicSourceConfig": { + "SubSonicSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -349,7 +413,7 @@ "type": "array" }, "data": { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicData" + "$ref": "#/definitions/SubsonicData" }, "name": { "description": "Unique identifier for this source.", @@ -361,7 +425,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/subsonic\",{assert:{\"resolution-mode\":\"import\"}}).SubsonicData": { + "SubsonicData": { "properties": { "maxPollRetries": { "default": 0, @@ -399,7 +463,7 @@ ], "type": "object" }, - "import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/tautulli\",{assert:{\"resolution-mode\":\"import\"}}).TautulliSourceConfig": { + "TautulliSourceConfig": { "properties": { "clients": { "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", @@ -411,21 +475,51 @@ "data": { "allOf": [ { - "$ref": "#/definitions/import(\"/home/foxx/code/multi-scrobbler/src/common/infrastructure/config/source/index\",{assert:{\"resolution-mode\":\"import\"}}).CommonSourceData" + "$ref": "#/definitions/CommonSourceData" }, { "properties": { "libraries": { - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" }, "servers": { - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" }, "user": { - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", - "type": "string" + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" } }, "type": "object" diff --git a/src/sources/ScrobbleSources.ts b/src/sources/ScrobbleSources.ts index 744e6806..6da23761 100644 --- a/src/sources/ScrobbleSources.ts +++ b/src/sources/ScrobbleSources.ts @@ -19,10 +19,8 @@ import {PlexSourceConfig} from "../common/infrastructure/config/source/plex.js"; import {SpotifySourceConfig, SpotifySourceData} from "../common/infrastructure/config/source/spotify.js"; import AbstractSource from "./AbstractSource.js"; import {AIOConfig} from "../common/infrastructure/config/aioConfig.js"; -import * as aioSchema from "../common/schema/aio.json" assert {type: "json"}; -import {CommonSourceData} from "../common/infrastructure/config/source/index.js"; -import {ClientConfig} from "../common/infrastructure/config/client/clients.js"; -import * as clientSchema from "../common/schema/client.json" assert {type: "json"}; +import * as aioSchema from "../common/schema/aio-source.json"; +import * as sourceSchema from "../common/schema/source.json"; import {LastfmSourceConfig} from "../common/infrastructure/config/source/lastfm.js"; type groupedNamedConfigs = {[key: string]: ParsedConfig[]}; @@ -224,7 +222,7 @@ export default class ScrobbleSources { } for (const [i,rawConf] of sourceConfigs.entries()) { try { - const validConfig = validateJson(rawConf, clientSchema, this.logger); + const validConfig = validateJson(rawConf, sourceSchema, this.logger); if(sourceType !== 'lastfm' || ((validConfig as LastfmSourceConfig).configureAs === 'source')) { // @ts-ignore diff --git a/src/utils.ts b/src/utils.ts index 8276e5dc..c131f243 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -136,7 +136,7 @@ const CWD = process.cwd(); let longestLabel = 3; export const defaultFormat = printf(({level, message, label = 'App', timestamp, [SPLAT]: splatObj, stack, ...rest}) => { - let stringifyValue = splatObj !== undefined ? stringify.default(splatObj) : ''; + let stringifyValue = splatObj !== undefined ? stringify(splatObj) : ''; if (label.length > longestLabel) { longestLabel = label.length; } @@ -392,8 +392,8 @@ export const parseDurationFromTimestamp = (timestamp: any) => { }); } -export const createAjvFactory = (logger: Logger): Ajv.default => { - const validator = new Ajv.default({logger: logger, verbose: true, strict: "log", allowUnionTypes: true}); +export const createAjvFactory = (logger: Logger): Ajv => { + const validator = new Ajv({logger: logger, verbose: true, strict: "log", allowUnionTypes: true}); // https://ajv.js.org/strict-mode.html#unknown-keywords validator.addKeyword('deprecationMessage'); return validator; diff --git a/tsconfig.json b/tsconfig.json index 1013d12e..c720e5b4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,9 +4,11 @@ "resolveJsonModule": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "esModuleInterop": false, + "lib": ["es2020"], + "module": "commonjs", + "target": "es2020", + "esModuleInterop": true, - "module": "NodeNext", "typeRoots": [ "./node_modules/@types", "./src/common/infrastructure/typings" -- 2.51.2 From 93230c45c7c3cc64d015660bbd1ef81d3fa9e7ff Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 11:52:27 -0500 Subject: [PATCH 52/64] docs: Add examples and more descriptions to json schema --- .../infrastructure/config/client/index.ts | 42 ++ .../infrastructure/config/client/lastfm.ts | 5 + .../infrastructure/config/client/maloja.ts | 4 + src/common/infrastructure/config/common.ts | 2 + .../infrastructure/config/source/deezer.ts | 6 + .../infrastructure/config/source/index.ts | 5 +- .../infrastructure/config/source/jellyfin.ts | 4 + .../infrastructure/config/source/lastfm.ts | 10 +- .../infrastructure/config/source/plex.ts | 48 +- .../infrastructure/config/source/spotify.ts | 6 + .../infrastructure/config/source/subsonic.ts | 13 + src/common/schema/aio-client.json | 89 ++- src/common/schema/aio-source.json | 410 +++++++++----- src/common/schema/aio.json | 533 +++++++++++++----- src/common/schema/client.json | 83 ++- src/common/schema/source.json | 401 ++++++++----- 16 files changed, 1199 insertions(+), 462 deletions(-) diff --git a/src/common/infrastructure/config/client/index.ts b/src/common/infrastructure/config/client/index.ts index 4b662934..5c4942cf 100644 --- a/src/common/infrastructure/config/client/index.ts +++ b/src/common/infrastructure/config/client/index.ts @@ -1,8 +1,29 @@ import {CommonConfig, CommonData} from "../common.js"; +/** + * Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging. + * */ export interface MatchLoggingOptions { + /** + * Log to DEBUG when a new track does NOT match an existing scrobble + * + * @default false + * @examples [false] + * */ onNoMatch?: boolean + /** + * Log to DEBUG when a new track DOES match an existing scrobble + * + * @default false + * @examples [false] + * */ onMatch?: boolean + /** + * Include confidence breakdowns in track match logging, if applicable + * + * @default false + * @examples [false] + * */ confidenceBreakdown?: boolean } @@ -11,19 +32,35 @@ export interface CommonClientData extends CommonData { * default # of http request retries a client can make before error is thrown. * * @default 1 + * @examples [1] * */ maxRequestRetries?: number /** * default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying). * * @default 1.5 + * @examples [1.5] * */ retryMultiplier?: number options?: { + /** + * Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history + * @default true + * @examples [true] + * */ refreshEnabled?: boolean + /** + * Check client for an existing scrobble at the same recorded time as the "new" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled. + * @default true + * @examples [true] + * */ checkExistingScrobbles?: boolean + /** + * Options used for increasing verbosity of logging in MS (used for debugging) + * */ verbose?: { + match?: MatchLoggingOptions } } @@ -32,7 +69,12 @@ export interface CommonClientData extends CommonData { export interface CommonClientConfig extends CommonConfig { /** * Unique identifier for this client. Used with sources to restrict where scrobbles are sent. + * + * @examples ["MyConfig"] * */ name: string + /** + * Specific data required to configure this client + * */ data?: CommonClientData } diff --git a/src/common/infrastructure/config/client/lastfm.ts b/src/common/infrastructure/config/client/lastfm.ts index b583a6df..636bd2f8 100644 --- a/src/common/infrastructure/config/client/lastfm.ts +++ b/src/common/infrastructure/config/client/lastfm.ts @@ -4,10 +4,14 @@ import {RequestRetryOptions} from "../common.js"; export interface LastfmData extends RequestRetryOptions { /** * API Key generated from Last.fm account + * + * @examples ["787c921a2a2ab42320831aba0c8f2fc2"] * */ apiKey: string /** * Secret generated from Last.fm account + * + * @examples ["ec42e09d5ae0ee0f0816ca151008412a"] * */ secret: string /** @@ -18,6 +22,7 @@ export interface LastfmData extends RequestRetryOptions { * Optional URI to use for callback. Specify this if callback should be different than the default. MUST have "lastfm/callback" in the URL somewhere. * * @default "http://localhost:9078/lastfm/callback" + * @examples ["http://localhost:9078/lastfm/callback"] * */ redirectUri?: string } diff --git a/src/common/infrastructure/config/client/maloja.ts b/src/common/infrastructure/config/client/maloja.ts index e9e060ff..fe43184e 100644 --- a/src/common/infrastructure/config/client/maloja.ts +++ b/src/common/infrastructure/config/client/maloja.ts @@ -4,10 +4,14 @@ import {RequestRetryOptions} from "../common.js"; export interface MalojaClientData extends RequestRetryOptions, CommonClientData { /** * URL for maloja server + * + * @examples ["http://localhost:42010"] * */ url: string /** * API Key for Maloja server + * + * @examples ["myApiKey"] * */ apiKey: string } diff --git a/src/common/infrastructure/config/common.ts b/src/common/infrastructure/config/common.ts index b8950014..835f7e48 100644 --- a/src/common/infrastructure/config/common.ts +++ b/src/common/infrastructure/config/common.ts @@ -13,12 +13,14 @@ export interface RequestRetryOptions { * default # of http request retries a source can make before error is thrown * * @default 1 + * @examples [1] * */ maxRequestRetries?: number /** * default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying) * * @default 1.5 + * @examples [1.5] * */ retryMultiplier?: number } diff --git a/src/common/infrastructure/config/source/deezer.ts b/src/common/infrastructure/config/source/deezer.ts index e117aa0c..90b33fa0 100644 --- a/src/common/infrastructure/config/source/deezer.ts +++ b/src/common/infrastructure/config/source/deezer.ts @@ -3,22 +3,28 @@ import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface DeezerData extends CommonSourceData { /** * deezer client id + * + * @examples ["a89cba1569901a0671d5a9875fed4be1"] * */ clientId: string /** * deezer client secret + * + * @examples ["ec42e09d5ae0ee0f0816ca151008412a"] * */ clientSecret: string /** * deezer redirect URI -- required only if not the default shown here. URI must end in "callback" * * @default "http://localhost:9078/deezer/callback" + * @examples ["http://localhost:9078/deezer/callback"] * */ redirectUri: string /** * optional, how long to wait before calling spotify for new tracks (in seconds) * * @default 60 + * @examples [60] * */ interval?: number } diff --git a/src/common/infrastructure/config/source/index.ts b/src/common/infrastructure/config/source/index.ts index 666071f1..cfc37249 100644 --- a/src/common/infrastructure/config/source/index.ts +++ b/src/common/infrastructure/config/source/index.ts @@ -5,6 +5,7 @@ export interface SourceRetryOptions extends RequestRetryOptions { * default # of automatic polling restarts on error * * @default 0 + * @examples [1] * */ maxPollRetries?: number } @@ -18,7 +19,9 @@ export interface CommonSourceConfig extends CommonConfig { * */ name?: string /** - * Restrict scrobbling tracks played from this source to Clients with names from this list + * Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients. + * + * @examples [["MyMalojaConfigName","MyLastFMConfigName"]] * */ clients?: string[] data?: CommonSourceData diff --git a/src/common/infrastructure/config/source/jellyfin.ts b/src/common/infrastructure/config/source/jellyfin.ts index 0f8f9606..807b441f 100644 --- a/src/common/infrastructure/config/source/jellyfin.ts +++ b/src/common/infrastructure/config/source/jellyfin.ts @@ -5,12 +5,16 @@ export interface JellyData extends CommonSourceData { * optional list of users to scrobble tracks from * * If none are provided tracks from all users will be scrobbled + * + * @examples [["MyUser1","MyUser2"]] * */ users?: string | string[] /** * optional list of servers to scrobble tracks from * * If none are provided tracks from all servers will be scrobbled + * + * @examples [["MyServerName1"]] * */ servers?: string | string[] } diff --git a/src/common/infrastructure/config/source/lastfm.ts b/src/common/infrastructure/config/source/lastfm.ts index e2fad00b..774ea6b7 100644 --- a/src/common/infrastructure/config/source/lastfm.ts +++ b/src/common/infrastructure/config/source/lastfm.ts @@ -1,9 +1,17 @@ import {LastfmData} from "../client/lastfm.js"; import {CommonSourceConfig, CommonSourceData} from "./index.js"; +export interface LastFmSourceData extends CommonSourceData, LastfmData{} + export interface LastfmSourceConfig extends CommonSourceConfig { + /** + * When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client. + * + * @default source + * @examples ["source"] + * */ configureAs?: 'source' - data: CommonSourceData & LastfmData + data: LastFmSourceData } export interface LastFmSouceAIOConfig extends LastfmSourceConfig { diff --git a/src/common/infrastructure/config/source/plex.ts b/src/common/infrastructure/config/source/plex.ts index 3ad04e80..d4ff98a4 100644 --- a/src/common/infrastructure/config/source/plex.ts +++ b/src/common/infrastructure/config/source/plex.ts @@ -1,26 +1,34 @@ import {CommonSourceConfig, CommonSourceData} from "./index.js"; +export interface PlexSourceData extends CommonSourceData { + /** + * optional list of users to scrobble tracks from + * + * If none are provided tracks from all users will be scrobbled + * + * @examples [["MyUser1", "MyUser2"]] + * */ + user?: string | string[] + /** + * optional list of libraries to scrobble tracks from + * + * If none are provided tracks from all libraries will be scrobbled + * + * @examples [["Audio","Music"]] + * */ + libraries?: string | string[] + /** + * optional list of servers to scrobble tracks from + * + * If none are provided tracks from all servers will be scrobbled + * + * @examples [["MyServerName"]] + * */ + servers?: string | string[] +} + export interface PlexSourceConfig extends CommonSourceConfig { - data: CommonSourceData & { - /** - * optional list of users to scrobble tracks from - * - * If none are provided tracks from all users will be scrobbled - * */ - user?: string | string[] - /** - * optional list of libraries to scrobble tracks from - * - * If none are provided tracks from all libraries will be scrobbled - * */ - libraries?: string | string[] - /** - * optional list of servers to scrobble tracks from - * - * If none are provided tracks from all servers will be scrobbled - * */ - servers?: string | string[] - } + data: PlexSourceData } export interface PlexSourceAIOConfig extends PlexSourceConfig { diff --git a/src/common/infrastructure/config/source/spotify.ts b/src/common/infrastructure/config/source/spotify.ts index 410d08aa..aa48b157 100644 --- a/src/common/infrastructure/config/source/spotify.ts +++ b/src/common/infrastructure/config/source/spotify.ts @@ -3,22 +3,28 @@ import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface SpotifySourceData extends CommonSourceData { /** * spotify client id + * + * @examples ["787c921a2a2ab42320831aba0c8f2fc2"] * */ clientId: string /** * spotify client secret + * + * @examples ["ec42e09d5ae0ee0f0816ca151008412a"] * */ clientSecret: string /** * spotify redirect URI -- required only if not the default shown here. URI must end in "callback" * * @default "http://localhost:9078/callback" + * @examples ["http://localhost:9078/callback"] * */ redirectUri: string /** * optional, how long to wait before calling spotify for new tracks (in seconds) * * @default 60 + * @examples [60] * */ interval?: number } diff --git a/src/common/infrastructure/config/source/subsonic.ts b/src/common/infrastructure/config/source/subsonic.ts index 4caadb48..f6a8ed63 100644 --- a/src/common/infrastructure/config/source/subsonic.ts +++ b/src/common/infrastructure/config/source/subsonic.ts @@ -3,9 +3,22 @@ import {CommonSourceConfig, CommonSourceData} from "./index.js"; export interface SubsonicData extends CommonSourceData { /** * URL of the subsonic media server to query + * + * @examples ["http://airsonic.local"] * */ url: string + /** + * Username to login to the server with + * + * @example ["MyUser"] + * */ user: string + + /** + * Password for the user to login to the server with + * + * @examples ["MyPassword"] + * */ password: string } export interface SubSonicSourceConfig extends CommonSourceConfig { diff --git a/src/common/schema/aio-client.json b/src/common/schema/aio-client.json index 57da8072..3826c7cc 100644 --- a/src/common/schema/aio-client.json +++ b/src/common/schema/aio-client.json @@ -16,17 +16,31 @@ "maxRequestRetries": { "default": 1, "description": "default # of http request retries a client can make before error is thrown.", + "examples": [ + 1 + ], "type": "number" }, "options": { "properties": { "checkExistingScrobbles": { + "default": true, + "description": "Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.", + "examples": [ + true + ], "type": "boolean" }, "refreshEnabled": { + "default": true, + "description": "Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history", + "examples": [ + true + ], "type": "boolean" }, "verbose": { + "description": "Options used for increasing verbosity of logging in MS (used for debugging)", "properties": { "match": { "$ref": "#/definitions/MatchLoggingOptions" @@ -40,6 +54,9 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying).", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -62,10 +79,14 @@ { "$ref": "#/definitions/LastfmData" } - ] + ], + "description": "Specific data required to configure this client" }, "name": { "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "examples": [ + "MyConfig" + ], "type": "string" }, "type": { @@ -86,25 +107,40 @@ "properties": { "apiKey": { "description": "API Key generated from Last.fm account", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], "type": "string" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "redirectUri": { "default": "http://localhost:9078/lastfm/callback", "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "examples": [ + "http://localhost:9078/lastfm/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "secret": { "description": "Secret generated from Last.fm account", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "session": { @@ -121,10 +157,14 @@ "MalojaClientAIOConfig": { "properties": { "data": { - "$ref": "#/definitions/MalojaClientData" + "$ref": "#/definitions/MalojaClientData", + "description": "Specific data required to configure this client" }, "name": { "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "examples": [ + "MyConfig" + ], "type": "string" }, "type": { @@ -145,22 +185,39 @@ "properties": { "apiKey": { "description": "API Key for Maloja server", + "examples": [ + "myApiKey" + ], "type": "string" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { "properties": { "checkExistingScrobbles": { + "default": true, + "description": "Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.", + "examples": [ + true + ], "type": "boolean" }, "refreshEnabled": { + "default": true, + "description": "Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history", + "examples": [ + true + ], "type": "boolean" }, "verbose": { + "description": "Options used for increasing verbosity of logging in MS (used for debugging)", "properties": { "match": { "$ref": "#/definitions/MatchLoggingOptions" @@ -174,10 +231,16 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "url": { "description": "URL for maloja server", + "examples": [ + "http://localhost:42010" + ], "type": "string" } }, @@ -188,14 +251,30 @@ "type": "object" }, "MatchLoggingOptions": { + "description": "Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.", "properties": { "confidenceBreakdown": { + "default": false, + "description": "Include confidence breakdowns in track match logging, if applicable", + "examples": [ + false + ], "type": "boolean" }, "onMatch": { + "default": false, + "description": "Log to DEBUG when a new track DOES match an existing scrobble", + "examples": [ + false + ], "type": "boolean" }, "onNoMatch": { + "default": false, + "description": "Log to DEBUG when a new track does NOT match an existing scrobble", + "examples": [ + false + ], "type": "boolean" } }, @@ -206,11 +285,17 @@ "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, diff --git a/src/common/schema/aio-source.json b/src/common/schema/aio-source.json index 669cd243..ead4086e 100644 --- a/src/common/schema/aio-source.json +++ b/src/common/schema/aio-source.json @@ -1,52 +1,44 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "CommonSourceData": { - "properties": { - "maxPollRetries": { - "default": 0, - "description": "default # of automatic polling restarts on error", - "type": "number" - }, - "maxRequestRetries": { - "default": 1, - "description": "default # of http request retries a source can make before error is thrown", - "type": "number" - }, - "options": { - "$ref": "#/definitions/Record" - }, - "retryMultiplier": { - "default": 1.5, - "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", - "type": "number" - } - }, - "type": "object" - }, "DeezerData": { "properties": { "clientId": { "description": "deezer client id", + "examples": [ + "a89cba1569901a0671d5a9875fed4be1" + ], "type": "string" }, "clientSecret": { "description": "deezer client secret", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "interval": { "default": 60, "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "examples": [ + 60 + ], "type": "number" }, "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -55,11 +47,17 @@ "redirectUri": { "default": "http://localhost:9078/deezer/callback", "description": "deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "examples": [ + "http://localhost:9078/deezer/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -73,7 +71,13 @@ "DeezerSourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -104,11 +108,17 @@ "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -117,6 +127,9 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "servers": { @@ -131,7 +144,12 @@ "type": "string" } ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "examples": [ + [ + "MyServerName1" + ] + ] }, "users": { "anyOf": [ @@ -145,7 +163,13 @@ "type": "string" } ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "examples": [ + [ + "MyUser1", + "MyUser2" + ] + ] } }, "type": "object" @@ -153,7 +177,13 @@ "JellySourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -182,27 +212,31 @@ "LastFmSouceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "configureAs": { + "default": "source", + "description": "When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.", "enum": [ "source" ], + "examples": [ + "source" + ], "type": "string" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "$ref": "#/definitions/LastfmData" - } - ] + "$ref": "#/definitions/LastFmSourceData" }, "name": { "description": "Unique identifier for this source.", @@ -221,29 +255,55 @@ ], "type": "object" }, - "LastfmData": { + "LastFmSourceData": { "properties": { "apiKey": { "description": "API Key generated from Last.fm account", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], "type": "string" }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], + "type": "number" + }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, + "options": { + "$ref": "#/definitions/Record" + }, "redirectUri": { "default": "http://localhost:9078/lastfm/callback", "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "examples": [ + "http://localhost:9078/lastfm/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "secret": { "description": "Secret generated from Last.fm account", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "session": { @@ -260,65 +320,20 @@ "PlexSourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "properties": { - "libraries": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" - }, - "servers": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" - }, - "user": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" - } - }, - "type": "object" - } - ] + "$ref": "#/definitions/PlexSourceData" }, "name": { "description": "Unique identifier for this source.", @@ -337,6 +352,97 @@ ], "type": "object" }, + "PlexSourceData": { + "properties": { + "libraries": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", + "examples": [ + [ + "Audio", + "Music" + ] + ] + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], + "type": "number" + }, + "servers": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "examples": [ + [ + "MyServerName" + ] + ] + }, + "user": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "examples": [ + [ + "MyUser1", + "MyUser2" + ] + ] + } + }, + "type": "object" + }, "Record": { "type": "object" }, @@ -370,16 +476,25 @@ "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -388,7 +503,13 @@ "SpotifySourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -418,25 +539,40 @@ "properties": { "clientId": { "description": "spotify client id", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], "type": "string" }, "clientSecret": { "description": "spotify client secret", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "interval": { "default": 60, "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "examples": [ + 60 + ], "type": "number" }, "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -445,11 +581,17 @@ "redirectUri": { "default": "http://localhost:9078/callback", "description": "spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "examples": [ + "http://localhost:9078/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -465,29 +607,46 @@ "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { "$ref": "#/definitions/Record" }, "password": { + "description": "Password for the user to login to the server with", + "examples": [ + "MyPassword" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "url": { "description": "URL of the subsonic media server to query", + "examples": [ + "http://airsonic.local" + ], "type": "string" }, "user": { + "description": "Username to login to the server with", "type": "string" } }, @@ -501,7 +660,13 @@ "SubsonicSourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -530,65 +695,20 @@ "TautulliSourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "properties": { - "libraries": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" - }, - "servers": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" - }, - "user": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" - } - }, - "type": "object" - } - ] + "$ref": "#/definitions/PlexSourceData" }, "name": { "description": "Unique identifier for this source.", diff --git a/src/common/schema/aio.json b/src/common/schema/aio.json index d5b8cf65..56e06277 100644 --- a/src/common/schema/aio.json +++ b/src/common/schema/aio.json @@ -16,17 +16,31 @@ "maxRequestRetries": { "default": 1, "description": "default # of http request retries a client can make before error is thrown.", + "examples": [ + 1 + ], "type": "number" }, "options": { "properties": { "checkExistingScrobbles": { + "default": true, + "description": "Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.", + "examples": [ + true + ], "type": "boolean" }, "refreshEnabled": { + "default": true, + "description": "Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history", + "examples": [ + true + ], "type": "boolean" }, "verbose": { + "description": "Options used for increasing verbosity of logging in MS (used for debugging)", "properties": { "match": { "$ref": "#/definitions/MatchLoggingOptions" @@ -40,29 +54,9 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying).", - "type": "number" - } - }, - "type": "object" - }, - "CommonSourceData": { - "properties": { - "maxPollRetries": { - "default": 0, - "description": "default # of automatic polling restarts on error", - "type": "number" - }, - "maxRequestRetries": { - "default": 1, - "description": "default # of http request retries a source can make before error is thrown", - "type": "number" - }, - "options": { - "$ref": "#/definitions/Record" - }, - "retryMultiplier": { - "default": 1.5, - "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -72,25 +66,40 @@ "properties": { "clientId": { "description": "deezer client id", + "examples": [ + "a89cba1569901a0671d5a9875fed4be1" + ], "type": "string" }, "clientSecret": { "description": "deezer client secret", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "interval": { "default": 60, "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "examples": [ + 60 + ], "type": "number" }, "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -99,11 +108,17 @@ "redirectUri": { "default": "http://localhost:9078/deezer/callback", "description": "deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "examples": [ + "http://localhost:9078/deezer/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -117,7 +132,13 @@ "DeezerSourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -148,11 +169,17 @@ "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -161,6 +188,9 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "servers": { @@ -175,7 +205,12 @@ "type": "string" } ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "examples": [ + [ + "MyServerName1" + ] + ] }, "users": { "anyOf": [ @@ -189,7 +224,13 @@ "type": "string" } ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "examples": [ + [ + "MyUser1", + "MyUser2" + ] + ] } }, "type": "object" @@ -197,7 +238,13 @@ "JellySourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -226,27 +273,31 @@ "LastFmSouceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "configureAs": { + "default": "source", + "description": "When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.", "enum": [ "source" ], + "examples": [ + "source" + ], "type": "string" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "$ref": "#/definitions/LastfmData" - } - ] + "$ref": "#/definitions/LastFmSourceData" }, "name": { "description": "Unique identifier for this source.", @@ -265,6 +316,68 @@ ], "type": "object" }, + "LastFmSourceData": { + "properties": { + "apiKey": { + "description": "API Key generated from Last.fm account", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], + "type": "string" + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "redirectUri": { + "default": "http://localhost:9078/lastfm/callback", + "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "examples": [ + "http://localhost:9078/lastfm/callback" + ], + "type": "string" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], + "type": "number" + }, + "secret": { + "description": "Secret generated from Last.fm account", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], + "type": "string" + }, + "session": { + "description": "Optional session id returned from a completed auth flow", + "type": "string" + } + }, + "required": [ + "apiKey", + "secret" + ], + "type": "object" + }, "LastfmClientAIOConfig": { "properties": { "configureAs": { @@ -282,10 +395,14 @@ { "$ref": "#/definitions/LastfmData" } - ] + ], + "description": "Specific data required to configure this client" }, "name": { "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "examples": [ + "MyConfig" + ], "type": "string" }, "type": { @@ -306,25 +423,40 @@ "properties": { "apiKey": { "description": "API Key generated from Last.fm account", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], "type": "string" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "redirectUri": { "default": "http://localhost:9078/lastfm/callback", "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "examples": [ + "http://localhost:9078/lastfm/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "secret": { "description": "Secret generated from Last.fm account", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "session": { @@ -341,10 +473,14 @@ "MalojaClientAIOConfig": { "properties": { "data": { - "$ref": "#/definitions/MalojaClientData" + "$ref": "#/definitions/MalojaClientData", + "description": "Specific data required to configure this client" }, "name": { "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "examples": [ + "MyConfig" + ], "type": "string" }, "type": { @@ -365,22 +501,39 @@ "properties": { "apiKey": { "description": "API Key for Maloja server", + "examples": [ + "myApiKey" + ], "type": "string" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { "properties": { "checkExistingScrobbles": { + "default": true, + "description": "Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.", + "examples": [ + true + ], "type": "boolean" }, "refreshEnabled": { + "default": true, + "description": "Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history", + "examples": [ + true + ], "type": "boolean" }, "verbose": { + "description": "Options used for increasing verbosity of logging in MS (used for debugging)", "properties": { "match": { "$ref": "#/definitions/MatchLoggingOptions" @@ -394,10 +547,16 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "url": { "description": "URL for maloja server", + "examples": [ + "http://localhost:42010" + ], "type": "string" } }, @@ -408,14 +567,30 @@ "type": "object" }, "MatchLoggingOptions": { + "description": "Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.", "properties": { "confidenceBreakdown": { + "default": false, + "description": "Include confidence breakdowns in track match logging, if applicable", + "examples": [ + false + ], "type": "boolean" }, "onMatch": { + "default": false, + "description": "Log to DEBUG when a new track DOES match an existing scrobble", + "examples": [ + false + ], "type": "boolean" }, "onNoMatch": { + "default": false, + "description": "Log to DEBUG when a new track does NOT match an existing scrobble", + "examples": [ + false + ], "type": "boolean" } }, @@ -424,65 +599,20 @@ "PlexSourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "properties": { - "libraries": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" - }, - "servers": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" - }, - "user": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" - } - }, - "type": "object" - } - ] + "$ref": "#/definitions/PlexSourceData" }, "name": { "description": "Unique identifier for this source.", @@ -501,6 +631,97 @@ ], "type": "object" }, + "PlexSourceData": { + "properties": { + "libraries": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", + "examples": [ + [ + "Audio", + "Music" + ] + ] + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], + "type": "number" + }, + "servers": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "examples": [ + [ + "MyServerName" + ] + ] + }, + "user": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "examples": [ + [ + "MyUser1", + "MyUser2" + ] + ] + } + }, + "type": "object" + }, "Record": { "type": "object" }, @@ -509,11 +730,17 @@ "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -549,16 +776,25 @@ "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -567,7 +803,13 @@ "SpotifySourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -597,25 +839,40 @@ "properties": { "clientId": { "description": "spotify client id", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], "type": "string" }, "clientSecret": { "description": "spotify client secret", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "interval": { "default": 60, "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "examples": [ + 60 + ], "type": "number" }, "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -624,11 +881,17 @@ "redirectUri": { "default": "http://localhost:9078/callback", "description": "spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "examples": [ + "http://localhost:9078/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -644,29 +907,46 @@ "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { "$ref": "#/definitions/Record" }, "password": { + "description": "Password for the user to login to the server with", + "examples": [ + "MyPassword" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "url": { "description": "URL of the subsonic media server to query", + "examples": [ + "http://airsonic.local" + ], "type": "string" }, "user": { + "description": "Username to login to the server with", "type": "string" } }, @@ -680,7 +960,13 @@ "SubsonicSourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -709,65 +995,20 @@ "TautulliSourceAIOConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "properties": { - "libraries": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" - }, - "servers": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" - }, - "user": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" - } - }, - "type": "object" - } - ] + "$ref": "#/definitions/PlexSourceData" }, "name": { "description": "Unique identifier for this source.", diff --git a/src/common/schema/client.json b/src/common/schema/client.json index c486c828..cdfa2002 100644 --- a/src/common/schema/client.json +++ b/src/common/schema/client.json @@ -14,17 +14,31 @@ "maxRequestRetries": { "default": 1, "description": "default # of http request retries a client can make before error is thrown.", + "examples": [ + 1 + ], "type": "number" }, "options": { "properties": { "checkExistingScrobbles": { + "default": true, + "description": "Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.", + "examples": [ + true + ], "type": "boolean" }, "refreshEnabled": { + "default": true, + "description": "Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history", + "examples": [ + true + ], "type": "boolean" }, "verbose": { + "description": "Options used for increasing verbosity of logging in MS (used for debugging)", "properties": { "match": { "$ref": "#/definitions/MatchLoggingOptions" @@ -38,6 +52,9 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying).", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -60,10 +77,14 @@ { "$ref": "#/definitions/LastfmData" } - ] + ], + "description": "Specific data required to configure this client" }, "name": { "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "examples": [ + "MyConfig" + ], "type": "string" } }, @@ -77,25 +98,40 @@ "properties": { "apiKey": { "description": "API Key generated from Last.fm account", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], "type": "string" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "redirectUri": { "default": "http://localhost:9078/lastfm/callback", "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "examples": [ + "http://localhost:9078/lastfm/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "secret": { "description": "Secret generated from Last.fm account", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "session": { @@ -112,10 +148,14 @@ "MalojaClientConfig": { "properties": { "data": { - "$ref": "#/definitions/MalojaClientData" + "$ref": "#/definitions/MalojaClientData", + "description": "Specific data required to configure this client" }, "name": { "description": "Unique identifier for this client. Used with sources to restrict where scrobbles are sent.", + "examples": [ + "MyConfig" + ], "type": "string" } }, @@ -129,22 +169,39 @@ "properties": { "apiKey": { "description": "API Key for Maloja server", + "examples": [ + "myApiKey" + ], "type": "string" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { "properties": { "checkExistingScrobbles": { + "default": true, + "description": "Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.", + "examples": [ + true + ], "type": "boolean" }, "refreshEnabled": { + "default": true, + "description": "Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history", + "examples": [ + true + ], "type": "boolean" }, "verbose": { + "description": "Options used for increasing verbosity of logging in MS (used for debugging)", "properties": { "match": { "$ref": "#/definitions/MatchLoggingOptions" @@ -158,10 +215,16 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "url": { "description": "URL for maloja server", + "examples": [ + "http://localhost:42010" + ], "type": "string" } }, @@ -172,14 +235,30 @@ "type": "object" }, "MatchLoggingOptions": { + "description": "Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.", "properties": { "confidenceBreakdown": { + "default": false, + "description": "Include confidence breakdowns in track match logging, if applicable", + "examples": [ + false + ], "type": "boolean" }, "onMatch": { + "default": false, + "description": "Log to DEBUG when a new track DOES match an existing scrobble", + "examples": [ + false + ], "type": "boolean" }, "onNoMatch": { + "default": false, + "description": "Log to DEBUG when a new track does NOT match an existing scrobble", + "examples": [ + false + ], "type": "boolean" } }, diff --git a/src/common/schema/source.json b/src/common/schema/source.json index 230c9bbd..044d1098 100644 --- a/src/common/schema/source.json +++ b/src/common/schema/source.json @@ -24,52 +24,44 @@ } ], "definitions": { - "CommonSourceData": { - "properties": { - "maxPollRetries": { - "default": 0, - "description": "default # of automatic polling restarts on error", - "type": "number" - }, - "maxRequestRetries": { - "default": 1, - "description": "default # of http request retries a source can make before error is thrown", - "type": "number" - }, - "options": { - "$ref": "#/definitions/Record" - }, - "retryMultiplier": { - "default": 1.5, - "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", - "type": "number" - } - }, - "type": "object" - }, "DeezerData": { "properties": { "clientId": { "description": "deezer client id", + "examples": [ + "a89cba1569901a0671d5a9875fed4be1" + ], "type": "string" }, "clientSecret": { "description": "deezer client secret", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "interval": { "default": 60, "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "examples": [ + 60 + ], "type": "number" }, "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -78,11 +70,17 @@ "redirectUri": { "default": "http://localhost:9078/deezer/callback", "description": "deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "examples": [ + "http://localhost:9078/deezer/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -96,7 +94,13 @@ "DeezerSourceConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -120,11 +124,17 @@ "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -133,6 +143,9 @@ "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "servers": { @@ -147,7 +160,12 @@ "type": "string" } ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "examples": [ + [ + "MyServerName1" + ] + ] }, "users": { "anyOf": [ @@ -161,7 +179,13 @@ "type": "string" } ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "examples": [ + [ + "MyUser1", + "MyUser2" + ] + ] } }, "type": "object" @@ -169,7 +193,13 @@ "JellySourceConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -188,29 +218,55 @@ ], "type": "object" }, - "LastfmData": { + "LastFmSourceData": { "properties": { "apiKey": { "description": "API Key generated from Last.fm account", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], "type": "string" }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], + "type": "number" + }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, + "options": { + "$ref": "#/definitions/Record" + }, "redirectUri": { "default": "http://localhost:9078/lastfm/callback", "description": "Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.", + "examples": [ + "http://localhost:9078/lastfm/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "secret": { "description": "Secret generated from Last.fm account", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "session": { @@ -227,27 +283,31 @@ "LastfmSourceConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "configureAs": { + "default": "source", + "description": "When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.", "enum": [ "source" ], + "examples": [ + "source" + ], "type": "string" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "$ref": "#/definitions/LastfmData" - } - ] + "$ref": "#/definitions/LastFmSourceData" }, "name": { "description": "Unique identifier for this source.", @@ -262,65 +322,20 @@ "PlexSourceConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "properties": { - "libraries": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" - }, - "servers": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" - }, - "user": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" - } - }, - "type": "object" - } - ] + "$ref": "#/definitions/PlexSourceData" }, "name": { "description": "Unique identifier for this source.", @@ -332,13 +347,110 @@ ], "type": "object" }, + "PlexSourceData": { + "properties": { + "libraries": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled", + "examples": [ + [ + "Audio", + "Music" + ] + ] + }, + "maxPollRetries": { + "default": 0, + "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], + "type": "number" + }, + "maxRequestRetries": { + "default": 1, + "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], + "type": "number" + }, + "options": { + "$ref": "#/definitions/Record" + }, + "retryMultiplier": { + "default": 1.5, + "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], + "type": "number" + }, + "servers": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled", + "examples": [ + [ + "MyServerName" + ] + ] + }, + "user": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled", + "examples": [ + [ + "MyUser1", + "MyUser2" + ] + ] + } + }, + "type": "object" + }, "Record": { "type": "object" }, "SpotifySourceConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -361,25 +473,40 @@ "properties": { "clientId": { "description": "spotify client id", + "examples": [ + "787c921a2a2ab42320831aba0c8f2fc2" + ], "type": "string" }, "clientSecret": { "description": "spotify client secret", + "examples": [ + "ec42e09d5ae0ee0f0816ca151008412a" + ], "type": "string" }, "interval": { "default": 60, "description": "optional, how long to wait before calling spotify for new tracks (in seconds)", + "examples": [ + 60 + ], "type": "number" }, "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { @@ -388,11 +515,17 @@ "redirectUri": { "default": "http://localhost:9078/callback", "description": "spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"", + "examples": [ + "http://localhost:9078/callback" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" } }, @@ -406,7 +539,13 @@ "SubSonicSourceConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, @@ -430,29 +569,46 @@ "maxPollRetries": { "default": 0, "description": "default # of automatic polling restarts on error", + "examples": [ + 1 + ], "type": "number" }, "maxRequestRetries": { "default": 1, "description": "default # of http request retries a source can make before error is thrown", + "examples": [ + 1 + ], "type": "number" }, "options": { "$ref": "#/definitions/Record" }, "password": { + "description": "Password for the user to login to the server with", + "examples": [ + "MyPassword" + ], "type": "string" }, "retryMultiplier": { "default": 1.5, "description": "default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)", + "examples": [ + 1.5 + ], "type": "number" }, "url": { "description": "URL of the subsonic media server to query", + "examples": [ + "http://airsonic.local" + ], "type": "string" }, "user": { + "description": "Username to login to the server with", "type": "string" } }, @@ -466,65 +622,20 @@ "TautulliSourceConfig": { "properties": { "clients": { - "description": "Restrict scrobbling tracks played from this source to Clients with names from this list", + "description": "Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.", + "examples": [ + [ + "MyMalojaConfigName", + "MyLastFMConfigName" + ] + ], "items": { "type": "string" }, "type": "array" }, "data": { - "allOf": [ - { - "$ref": "#/definitions/CommonSourceData" - }, - { - "properties": { - "libraries": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled" - }, - "servers": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled" - }, - "user": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "string" - } - ], - "description": "optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled" - } - }, - "type": "object" - } - ] + "$ref": "#/definitions/PlexSourceData" }, "name": { "description": "Unique identifier for this source.", -- 2.51.2 From e960438968b0ecefa45e9be46464f68c22029e2d Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 12:27:27 -0500 Subject: [PATCH 53/64] feat: Revert specific config requirement to be a top-level array Wasn't really necessary and would cause breaking change for existing configs. --- src/clients/ScrobbleClients.ts | 4 +++- src/sources/ScrobbleSources.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clients/ScrobbleClients.ts b/src/clients/ScrobbleClients.ts index a3f6a1e6..d1036d02 100644 --- a/src/clients/ScrobbleClients.ts +++ b/src/clients/ScrobbleClients.ts @@ -137,8 +137,10 @@ export default class ScrobbleClients { } else if(rawClientConfigs === null) { this.logger.error(`${clientType}.json contained no data`); continue; + } else if(typeof rawClientConfigs === 'object') { + clientConfigs = [rawClientConfigs]; } else { - this.logger.error(`All top level data from ${clientType}.json must be an array of objects, will not parse configs from file`); + this.logger.error(`All top level data from ${clientType}.json must be an object or an array of objects, will not parse configs from file`); continue; } for(const [i,rawConf] of rawClientConfigs.entries()) { diff --git a/src/sources/ScrobbleSources.ts b/src/sources/ScrobbleSources.ts index 6da23761..7df03e77 100644 --- a/src/sources/ScrobbleSources.ts +++ b/src/sources/ScrobbleSources.ts @@ -216,8 +216,10 @@ export default class ScrobbleSources { } else if (rawSourceConfigs === null) { this.logger.error(`${sourceType}.json contained no data`); continue; + } else if (typeof rawSourceConfigs === 'object') { + sourceConfigs = [rawSourceConfigs]; } else { - this.logger.error(`All top level data from ${sourceType}.json must be an array of objects, will not parse configs from file`); + this.logger.error(`All top level data from ${sourceType}.json must be an object or an array of objects, will not parse configs from file`); continue; } for (const [i,rawConf] of sourceConfigs.entries()) { -- 2.51.2 From 360b6f4cf006b559e05d120483ec41fc38f474e4 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 12:27:55 -0500 Subject: [PATCH 54/64] docs: Add links to schema explorer for json configs --- docs/configuration.md | 20 ++++++++++--------- package.json | 4 ++-- .../infrastructure/config/client/lastfm.ts | 6 ++++++ src/common/schema/aio-client.json | 5 +++++ src/common/schema/aio.json | 5 +++++ src/common/schema/client.json | 5 +++++ 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 28281615..192c6463 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -79,6 +79,8 @@ There is also a [**kitchensink example**](/docs/kitchensink.md) that provides ex ### All-in-One File Configuration +[**Explore the schema for this configuration, along with an example generator and validator, here**](https://json-schema.app/view/%23?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Faio.json) + Example directory structure: ``` @@ -183,7 +185,7 @@ Client ID/Secret. Make sure to also whitelist your redirect URI in the applicati ### JSON-Based -See [`spotify.json.example`](/config/spotify.json.example) +See [`spotify.json.example`](/config/spotify.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FSpotifySourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) ## [Plex](https://plex.tv) @@ -197,7 +199,7 @@ Check the [instructions](plex.md) on how to setup a [webhooks](https://support.p ### JSON-Based -See [`plex.json.example`](/config/plex.json.example) +See [`plex.json.example`](/config/plex.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FPlexSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) ## [Tautulli](https://tautulli.com) @@ -211,7 +213,7 @@ Check the [instructions](plex.md) on how to setup a notification agent. ### JSON-Based -See [`tautulli.json.example`](/config/tautulli.json.example) +See [`tautulli.json.example`](/config/tautulli.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FTautulliSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) ## [Subsonic](http://www.subsonic.org/) @@ -234,7 +236,7 @@ Can use this source for any application that implements the [Subsonic API](http: ### JSON-Based -See [`subsonic.json.example`](/config/subsonic.json.example) +See [`subsonic.json.example`](/config/subsonic.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FSubSonicSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) ## [Jellyfin](https://jellyfin.org/) @@ -262,7 +264,7 @@ Must be using Jellyfin 10.7 or greater ### JSON-Based -See [`jellyfin.json.example`](/config/jellyfin.json.example) +See [`jellyfin.json.example`](/config/jellyfin.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FJellySourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) ## [Last.fm (Source)](https://www.last.fm) @@ -274,7 +276,7 @@ No support for ENV based for Last.fm as a client (only source) ### JSON-Based -See [`lastfm.json.example`](/config/lastfm.json.example), change `configureAs` to `source`. +See [`lastfm.json.example`](/config/lastfm.json.example), change `configureAs` to `source`. Or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FLastfmSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) ## [Deezer](https://deezer.com/) @@ -304,7 +306,7 @@ After starting multi-scrobbler with credentials in-place open the dashboard (`ht ### JSON-Based -See [`deezer.json.example`](/config/deezer.json.example) +See [`deezer.json.example`](/config/deezer.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FDeezerSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) # Client Configurations @@ -319,7 +321,7 @@ See [`deezer.json.example`](/config/deezer.json.example) ### JSON-Based -See [`maloja.json.example`](/config/maloja.json.example) +See [`maloja.json.example`](/config/maloja.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FMalojaClientConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fclient.json) ## [Last.fm](https://www.last.fm) @@ -342,4 +344,4 @@ or replace `localhost:9078` with your own base URL ### JSON-Based -See [`lastfm.json.example`](/config/lastfm.json.example) +See [`lastfm.json.example`](/config/lastfm.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FLastfmClientConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fclient.json) diff --git a/package.json b/package.json index 913703b3..7f2aaccf 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "test": "echo \"Error: no test specified\" && exit 1", "build": "tsc", "start": "node src/index.js", - "schema": "npm run -s schema-aio & npm run -s schema-sources & npm run -s schema-client & npm run -s schema-aiosource & npm run -s schema-aioclient", + "schema": "npm run -s schema-aio & npm run -s schema-source & npm run -s schema-client & npm run -s schema-aiosource & npm run -s schema-aioclient", "schema-client": "typescript-json-schema tsconfig.json ClientConfig --out src/common/schema/client.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", - "schema-sources": "typescript-json-schema tsconfig.json SourceConfig --out src/common/schema/source.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", + "schema-source": "typescript-json-schema tsconfig.json SourceConfig --out src/common/schema/source.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", "schema-aio": "typescript-json-schema tsconfig.json AIOConfig --out src/common/schema/aio.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", "schema-aiosource": "typescript-json-schema tsconfig.json AIOSourceConfig --out src/common/schema/aio-source.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage", "schema-aioclient": "typescript-json-schema tsconfig.json AIOClientConfig --out src/common/schema/aio-client.json --required --tsNodeRegister --refs --validationKeywords deprecationMessage" diff --git a/src/common/infrastructure/config/client/lastfm.ts b/src/common/infrastructure/config/client/lastfm.ts index 636bd2f8..a77fafe7 100644 --- a/src/common/infrastructure/config/client/lastfm.ts +++ b/src/common/infrastructure/config/client/lastfm.ts @@ -28,6 +28,12 @@ export interface LastfmData extends RequestRetryOptions { } export interface LastfmClientConfig extends CommonClientConfig { + /** + * Should always be `client` when using LastFM as a client + * + * @default client + * @examples ["client"] + * */ configureAs?: 'client' | 'source' data: CommonClientData & LastfmData } diff --git a/src/common/schema/aio-client.json b/src/common/schema/aio-client.json index 3826c7cc..6c3ccdff 100644 --- a/src/common/schema/aio-client.json +++ b/src/common/schema/aio-client.json @@ -65,10 +65,15 @@ "LastfmClientAIOConfig": { "properties": { "configureAs": { + "default": "client", + "description": "Should always be `client` when using LastFM as a client", "enum": [ "client", "source" ], + "examples": [ + "client" + ], "type": "string" }, "data": { diff --git a/src/common/schema/aio.json b/src/common/schema/aio.json index 56e06277..435e0d62 100644 --- a/src/common/schema/aio.json +++ b/src/common/schema/aio.json @@ -381,10 +381,15 @@ "LastfmClientAIOConfig": { "properties": { "configureAs": { + "default": "client", + "description": "Should always be `client` when using LastFM as a client", "enum": [ "client", "source" ], + "examples": [ + "client" + ], "type": "string" }, "data": { diff --git a/src/common/schema/client.json b/src/common/schema/client.json index cdfa2002..ffbe8205 100644 --- a/src/common/schema/client.json +++ b/src/common/schema/client.json @@ -63,10 +63,15 @@ "LastfmClientConfig": { "properties": { "configureAs": { + "default": "client", + "description": "Should always be `client` when using LastFM as a client", "enum": [ "client", "source" ], + "examples": [ + "client" + ], "type": "string" }, "data": { -- 2.51.2 From 4b1fa710f72f3654c059bc8b937fcd66997fd695 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 12:47:56 -0500 Subject: [PATCH 55/64] docs: Rename json-based to file-based for consistent terminology --- docs/configuration.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 192c6463..d1f81936 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -6,32 +6,32 @@ * [Source Configurations](#source-configurations) * [Spotify](#spotify) * [ENV-Based](#env-based) - * [JSON-Based](#json-based) + * [File-Based](#file-based) * [Plex](#plex) * [ENV-Based](#env-based-1) - * [JSON-Based](#json-based-1) + * [File-Based](#file-based-1) * [Tautulli](#tautulli) * [ENV-Based](#env-based-2) - * [JSON-Based](#json-based-2) + * [File-Based](#file-based-2) * [Subsonic](#subsonic) * [ENV-Based](#env-based-3) - * [JSON-Based](#json-based-3) + * [File-Based](#file-based-3) * [Jellyfin](#jellyfin) * [ENV-Based](#env-based-4) - * [JSON-Based](#json-based-4) + * [File-Based](#file-based-4) * [Last.fm (Source)](#lastfm--source-) * [ENV-Based](#env-based-5) - * [JSON-Based](#json-based-5) + * [File-Based](#file-based-5) * [Deezer](#deezer) * [ENV-Based](#env-based-6) - * [JSON-Based](#json-based-6) + * [File-Based](#file-based-6) * [Client Configurations](#client-configurations) * [Maloja](#maloja) * [ENV-Based](#env-based-7) - * [JSON-Based](#json-based-7) + * [File-Based](#file-based-7) * [Last.fm](#lastfm) * [ENV-Based](#env-based-8) - * [JSON-Based](#json-based-8) + * [File-Based](#file-based-8) # Configuration Overview @@ -183,7 +183,7 @@ Client ID/Secret. Make sure to also whitelist your redirect URI in the applicati | `SPOTIFY_CLIENT_SECRET` | Yes | | | | `SPOTIFY_REDIRECT_URI` | No | `http://localhost:9078/callback` | URI must end in `callback` | -### JSON-Based +### File-Based See [`spotify.json.example`](/config/spotify.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FSpotifySourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) @@ -197,7 +197,7 @@ Check the [instructions](plex.md) on how to setup a [webhooks](https://support.p |------------------------|----------|---------|-------------------------------------------------| | `PLEX_USER` | No | | The a comma-delimited list of usernames to scrobble tracks for. No usernames specified means all tracks by all users will be scrobbled. | -### JSON-Based +### File-Based See [`plex.json.example`](/config/plex.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FPlexSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) @@ -211,7 +211,7 @@ Check the [instructions](plex.md) on how to setup a notification agent. |------------------------|----------|---------|-------------------------------------------------| | `TAUTULLI_USER` | No | | The a comma-delimited list of usernames to scrobble tracks for. No usernames specified means all tracks by all users will be scrobbled. | -### JSON-Based +### File-Based See [`tautulli.json.example`](/config/tautulli.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FTautulliSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) @@ -234,7 +234,7 @@ Can use this source for any application that implements the [Subsonic API](http: | `SUBSONIC_PASSWORD` | Yes | | | | `SUBSONIC_URL` | Yes | | Base url of your subsonic-api server | -### JSON-Based +### File-Based See [`subsonic.json.example`](/config/subsonic.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FSubSonicSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) @@ -262,7 +262,7 @@ Must be using Jellyfin 10.7 or greater | `JELLYFIN_USER` | | | Comma-separated list of usernames (from Jellyfin) to scrobble for | | `JELLYFIN_SERVER` | | | Comma-separated list of Jellyfin server names to scrobble from | -### JSON-Based +### File-Based See [`jellyfin.json.example`](/config/jellyfin.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FJellySourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) @@ -274,7 +274,7 @@ See the [Last.fm (Client)](#lastfm) setup for registration instructions. No support for ENV based for Last.fm as a client (only source) -### JSON-Based +### File-Based See [`lastfm.json.example`](/config/lastfm.json.example), change `configureAs` to `source`. Or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FLastfmSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) @@ -304,7 +304,7 @@ After starting multi-scrobbler with credentials in-place open the dashboard (`ht | `DEEZER_CLIENT_SECRET` | Yes | | Your **Secret Key** | | `DEEZER_REDIRECT_URI` | No | `http://localhost:9078/deezer/callback` | URI must end in `deezer/callback` | -### JSON-Based +### File-Based See [`deezer.json.example`](/config/deezer.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FDeezerSourceConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json) @@ -319,7 +319,7 @@ See [`deezer.json.example`](/config/deezer.json.example) or [explore the schema | `MALOJA_URL` | Yes | | Base URL of your installation | | `MALOJA_API_KEY` | Yes | | Api Key | -### JSON-Based +### File-Based See [`maloja.json.example`](/config/maloja.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FMalojaClientConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fclient.json) @@ -342,6 +342,6 @@ or replace `localhost:9078` with your own base URL | `LASTFM_REDIRECT_URI` | No | `http://localhost:9078/lastfm/callback` | Url to use for authentication. Must include `lastfm/callback` somewhere in it | | `LASTFM_SESSION` | No | | Session id. Will be generated by authentication flow if not provided. | -### JSON-Based +### File-Based See [`lastfm.json.example`](/config/lastfm.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23/%23%2Fdefinitions%2FLastfmClientConfig?url=https%3A%2F%2Fraw.githubusercontent.com%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fclient.json) -- 2.51.2 From 1b199750bd9b5dc7ac16aae06c40523d42c21e2e Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 12:49:20 -0500 Subject: [PATCH 56/64] docs: Simplify and correct example configs * Keep examples as valid json by removing comments and fixing trailing commas #55 * Add README pointing to configuration docs --- config/README.md | 12 ++++++++++++ config/config.json.example | 35 ++++++++++++++++++----------------- config/deezer.json.example | 11 +++++------ config/jellyfin.json.example | 8 ++++---- config/lastfm.json.example | 15 +++++---------- config/maloja.json.example | 7 +++---- config/plex.json.example | 10 +++++----- config/spotify.json.example | 13 ++++++------- config/subsonic.json.example | 9 ++++----- config/tautulli.json.example | 10 +++++----- 10 files changed, 67 insertions(+), 63 deletions(-) create mode 100644 config/README.md diff --git a/config/README.md b/config/README.md new file mode 100644 index 00000000..e2ddaa52 --- /dev/null +++ b/config/README.md @@ -0,0 +1,12 @@ +These are **example configurations** for all Source/Client types and AIO config. + +These can be used as-is by renaming them to `.json` and filling or replacing sample data. + +For docker installations these examples are copied to your configuration directory on first-time use. + +These are **NOT** exhaustive examples. You should consult the **configuration** documentation and the **schema explorer links** for each source/config type to see a complete list of options and descriptions for all properties. + +Documentation at + +* [include docs](/docs/configuration.md) +* External Link: https://github.com/FoxxMD/multi-scrobbler/blob/master/docs/configuration.md diff --git a/config/config.json.example b/config/config.json.example index 53da4cfa..4f22d969 100644 --- a/config/config.json.example +++ b/config/config.json.example @@ -1,32 +1,33 @@ { "sourceDefaults": { - "maxPollRetries": 0, // optional, default # of automatic polling restarts on error. can be overridden by property in individual config - "maxRequestRetries": 1, // optional, default # of http request retries a source can make before error is thrown. can be overridden by property in individual config - "retryMultiplier": 1.5, // optional, default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying). can be overridden by property in individual config + "maxPollRetries": 0, + "maxRequestRetries": 1, + "retryMultiplier": 1.5 }, "clientDefaults": { - "maxRequestRetries": 1, // optional, default # of http request retries a client can make before error is thrown. can be overridden by property in individual config - "retryMultiplier": 1.5, // optional, default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying). can be overridden by property in individual config + "maxRequestRetries": 1, + "retryMultiplier": 1.5 }, "sources": [ { - "type": "spotify", // required, source type - "clients": ["myConfig"], // optional, a list of Client config names this Source should scrobble to. Using an empty list or not including this property will make this Source scrobble to all Clients. - "name": "mySpotifySource", // optional, friendly name for the log - "data": { // required, the data for your config - "clientId": "example", - //... + "type": "spotify", + "clients": ["myConfig"], + "name": "mySpotifySource", + "data": { + "clientId": "a89cba1569901a0671d5a9875fed4be1", + "clientSecret": "ec42e09d5ae0ee0f0816ca151008412a", + "redirectUri": "http://localhost:9078/callback" } } ], "clients": [ { - "type": "maloja", // required, Client type - "name": "myConfig", // required, a name to identifier your Client - "data": { // required, the data for your config - "url": "http://example.com", - //... + "type": "maloja", + "name": "myConfig", + "data": { + "url": "http://localhost:42010", + "apiKey": "myMalojaKey" } - }, + } ] } diff --git a/config/deezer.json.example b/config/deezer.json.example index eb890025..2a8ec885 100644 --- a/config/deezer.json.example +++ b/config/deezer.json.example @@ -1,13 +1,12 @@ [ { "name": "FoxxMDeezer", - "clients": [], // optional, list of scrobble clients (by config name) that this source should scrobble to. Using an empty list or not including this property will make this source scrobble to all clients. + "clients": [], "data": { - "clientId": "string", // deezer APPLICATION ID - "clientSecret": "string", // deezer SECRET KEY - "redirectUri": "http://localhost:9078/deezer/callback",// deezer redirect URI -- required only if not the default shown here. URI must end in "deezer/callback" - "interval": 60, // optional, how long to wait before calling spotify for new tracks - // ALSO see config.json.example for default properties that can be overridden here (in sourceDefaults) + "clientId": "a89cba1569901a0671d5a9875fed4be1", + "clientSecret": "ec42e09d5ae0ee0f0816ca151008412a", + "redirectUri": "http://localhost:9078/deezer/callback", + "interval": 60 } } ] diff --git a/config/jellyfin.json.example b/config/jellyfin.json.example index 48e3fbae..44357c84 100644 --- a/config/jellyfin.json.example +++ b/config/jellyfin.json.example @@ -1,10 +1,10 @@ [ { - "name": "default", // optional, friendly name for logs - "clients": [], // optional, list of scrobble clients (by config name) that this source should scrobble to. Using an empty list or not including this property will make this source scrobble to all clients. + "name": "MyJellyfin", + "clients": [], "data": { - "users": ["FoxxMD"], // optional, list of users to scrobble tracks for - "servers": ["myServer","anotherServer"] // optional, list of servers to scrobble tracks from + "users": ["FoxxMD"], + "servers": ["myServer","anotherServer"] } } ] diff --git a/config/lastfm.json.example b/config/lastfm.json.example index b293b0a3..a63fa977 100644 --- a/config/lastfm.json.example +++ b/config/lastfm.json.example @@ -1,16 +1,11 @@ [ { - "name": "myLastFm", // [As Client/Source] required if configured as "client", a name to identify your Client/Source - "configureAs": "client", // optional and default to "client", set to "source" to use this configuration as a Source - "clients": [], // [As Source] optional, list of scrobble Clients (by config name) that this Source should scrobble to. Using an empty list or not including this property will make this Source scrobble to all Clients. + "name": "myLastFm", + "configureAs": "client", "data": { - "apiKey": "string", // required, Lastfm api key - "secret": "string", // required, Lastfm shared secret - "session": "string", // optional, session id returned from a complete auth flow. - // if not specified will be generated during authentication - "redirectUri": "http://localhost:9078/lastfm/callback" // optional, if not different than this default - // callback for auth. Must have "lastfm/callback" in the url somewhere - // ALSO see config.json.example for default properties that can be overridden here (in clientDefaults) + "apiKey": "a89cba1569901a0671d5a9875fed4be1", + "secret": "ec42e09d5ae0ee0f0816ca151008412a", + "redirectUri": "http://localhost:9078/lastfm/callback" } } ] diff --git a/config/maloja.json.example b/config/maloja.json.example index cf27656b..7c7e2a73 100644 --- a/config/maloja.json.example +++ b/config/maloja.json.example @@ -1,10 +1,9 @@ [ { - "name": "myMaloja", // required, a name to identify your Client + "name": "myMaloja", "data": { - "url": "https://domain.tld", // required, the base url of your maloja installation - "apiKey": "string" // required, your maloja api key - // ALSO see config.json.example for default properties that can be overridden here (in clientDefaults) + "url": "http://localhost:42010", + "apiKey": "myMalojaKey" } } ] diff --git a/config/plex.json.example b/config/plex.json.example index 652eeb53..5c8b15ce 100644 --- a/config/plex.json.example +++ b/config/plex.json.example @@ -1,11 +1,11 @@ [ { - "name": "default", // optional, friendly name for logs - "clients": [], // optional, list of scrobble clients (by config name) that this source should scrobble to. Using an empty list or not including this property will make this source scrobble to all clients. + "name": "MyPlex", + "clients": [], "data": { - "user": ["username@gmail.com","anotherUser@gmail.com"], // optional, list of users to scrobble tracks for - "libraries": ["music","my podcasts"], // optional, list of libraries to scrobble tracks from - "servers": ["myServer","anotherServer"] // optional, list of servers to scrobble tracks from + "user": ["username@gmail.com","anotherUser@gmail.com"], + "libraries": ["music","my podcasts"], + "servers": ["myServer","anotherServer"] } } ] diff --git a/config/spotify.json.example b/config/spotify.json.example index bb9e1b93..4a2594fe 100644 --- a/config/spotify.json.example +++ b/config/spotify.json.example @@ -1,13 +1,12 @@ [ { - "name": "default", // optional, friendly name for logs - "clients": [], // optional, list of scrobble clients (by config name) that this source should scrobble to. Using an empty list or not including this property will make this source scrobble to all clients. + "name": "MySpotify", + "clients": [], "data": { - "clientId": "string", // spotify client id - "clientSecret": "string", // spotify client secret - "redirectUri": "http://localhost:9078/callback",// spotify redirect URI -- required only if not the default shown here. URI must end in "callback" - "interval": 60, // optional, how long to wait before calling spotify for new tracks - // ALSO see config.json.example for default properties that can be overridden here (in sourceDefaults) + "clientId": "a89cba1569901a0671d5a9875fed4be1", + "clientSecret": "ec42e09d5ae0ee0f0816ca151008412a", + "redirectUri": "http://localhost:9078/callback", + "interval": 60 } } ] diff --git a/config/subsonic.json.example b/config/subsonic.json.example index 88799809..e306478f 100644 --- a/config/subsonic.json.example +++ b/config/subsonic.json.example @@ -1,11 +1,10 @@ [ { - "name": "default", // optional, friendly name for logs + "name": "MySubsonic", "data": { - "url": "http://localhost:4040/airsonic",// required, the url you would visit to listen to music on the web - "user": "yourUser", // required, username to login with - "password": "yourPassword", // required, password to login with - // ALSO see config.json.example for default properties that can be overridden here (in sourceDefaults) + "url": "http://localhost:4040/airsonic", + "user": "yourUser", + "password": "yourPassword", } } ] diff --git a/config/tautulli.json.example b/config/tautulli.json.example index 652eeb53..811976c5 100644 --- a/config/tautulli.json.example +++ b/config/tautulli.json.example @@ -1,11 +1,11 @@ [ { - "name": "default", // optional, friendly name for logs - "clients": [], // optional, list of scrobble clients (by config name) that this source should scrobble to. Using an empty list or not including this property will make this source scrobble to all clients. + "name": "MyTautuilli", + "clients": [], "data": { - "user": ["username@gmail.com","anotherUser@gmail.com"], // optional, list of users to scrobble tracks for - "libraries": ["music","my podcasts"], // optional, list of libraries to scrobble tracks from - "servers": ["myServer","anotherServer"] // optional, list of servers to scrobble tracks from + "user": ["username@gmail.com","anotherUser@gmail.com"], + "libraries": ["music","my podcasts"], + "servers": ["myServer","anotherServer"] } } ] -- 2.51.2 From 931709d277b941b45ba79eaf8b3afdf559cf8f67 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 12:50:11 -0500 Subject: [PATCH 57/64] fix: typo --- config/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/README.md b/config/README.md index e2ddaa52..cf84683b 100644 --- a/config/README.md +++ b/config/README.md @@ -8,5 +8,5 @@ These are **NOT** exhaustive examples. You should consult the **configuration** Documentation at -* [include docs](/docs/configuration.md) +* [internal docs](/docs/configuration.md) * External Link: https://github.com/FoxxMD/multi-scrobbler/blob/master/docs/configuration.md -- 2.51.2 From 89540d8b7688b85625da137e0d8fbaa060377e85 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 21 Feb 2023 12:54:40 -0500 Subject: [PATCH 58/64] docs: Add jellyfin example to kitchen sink and fix inconsistencies #55 --- docs/kitchensink.md | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/kitchensink.md b/docs/kitchensink.md index 89c83287..85741607 100644 --- a/docs/kitchensink.md +++ b/docs/kitchensink.md @@ -11,6 +11,7 @@ Scenario: * Fred has a Plex server and wants to scrobble everything he plays * Mary uses Fred's Plex server but only wants to scrobble her plays from the `podcast` library * The three of you have a shared library on Plex called `party` that you only play when you are hanging out. You want plays from that library to be scrobbled to everyone's servers. +* Fred also has his own Jellyfin server and wants to scrobble everything he plays ### All-in-one Config @@ -82,6 +83,14 @@ Using just one config file located at `CONFIG_DIR/config.json`: "libraries": ["party"], } }, + { + "type": "jellyfin", + "name": "FredJelly", + // omitting clients (or making it empty) will make this Source scrobble to all Clients + "data": { + "user": ["fred@email.com"] + } + }, { "type": "subsonic", "name": "foxxAirsonic", @@ -124,6 +133,7 @@ Using just one config file located at `CONFIG_DIR/config.json`: "data": { "apiKey": "maryApiKey", "secret": "marySecret", + "redirectUri": "http://localhost:9078/lastfm/callback" } } ] @@ -143,7 +153,7 @@ In `CONFIG_DIR/spotify.json`: "clients": ["foxxMaloja"], "data": { "clientId": "foxxSpotifyAppId", - "clientSecret": "foxxSpotifyAppSecret", + "clientSecret": "foxxSpotifyAppSecret" } }, { @@ -151,7 +161,7 @@ In `CONFIG_DIR/spotify.json`: "clients": ["maryMaloja"], "data": { "clientId": "foxxSpotifyAppId", - "clientSecret": "foxxSpotifyAppSecret", + "clientSecret": "foxxSpotifyAppSecret" } }, { @@ -160,7 +170,7 @@ In `CONFIG_DIR/spotify.json`: "data": { "accessToken": "fredsToken", "refreshToken": "fredsRefreshToken", - "interval": 120, + "interval": 120 } }, ] @@ -188,7 +198,20 @@ In `CONFIG_DIR/plex.json` { "name": "partyPlex", "data": { - "libraries": ["party"], + "libraries": ["party"] + } + } +] +``` + +In `CONFIG_DIR/jellyfin.json` + +```json5 +[ + { + "name": "FredJelly", + "data": { + "user": ["fred@email.com"] } } ] @@ -231,6 +254,7 @@ In `CONFIG_DIR/lastfm.json`: "data": { "apiKey": "maryApiKey", "secret": "marySecret", + "redirectUri": "http://localhost:9078/lastfm/callback" } } ] -- 2.51.2 From da4d2ecadeae442991e285c4edeb8f8617770119 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 22 Feb 2023 11:01:00 -0500 Subject: [PATCH 59/64] docs: Add additional properties to tautulli payload --- docs/plex.md | 5 ++++- src/sources/TautulliSource.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/plex.md b/docs/plex.md index 08a1acce..c619da6f 100644 --- a/docs/plex.md +++ b/docs/plex.md @@ -41,7 +41,10 @@ Expand the **Watched** dropdown and add the following code block to the **JSON D "media_type": "{media_type}", "title": "{title}", "duration": "{duration_sec}", -"username": "{username}" +"username": "{username}", +"server": "{server_name}", +"version": "{server_version}", +"library": "{library_name}" } ``` diff --git a/src/sources/TautulliSource.ts b/src/sources/TautulliSource.ts index 744d5dbd..bde90f01 100644 --- a/src/sources/TautulliSource.ts +++ b/src/sources/TautulliSource.ts @@ -20,8 +20,10 @@ export default class TautulliSource extends PlexSource { title, library_name, server, + version, duration, username, + library } = obj; let artists = [artist_name]; if (track_artist !== undefined && track_artist !== artist_name) { @@ -37,8 +39,9 @@ export default class TautulliSource extends PlexSource { }, meta: { title, - library: library_name, + library: library_name ?? library, server, + sourceVersion: version, mediaType: media_type, user: username, trackLength: duration, -- 2.51.2 From 2b798e5a69d587b140687963f8ed3405739e8c82 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 22 Feb 2023 11:04:50 -0500 Subject: [PATCH 60/64] feat: Implement initial request logging for all sources that use ingress instead of polling In order to make basic troubleshooting easier all ingress-based sources (plex, tautulli, jellyfin) now log initial connections and basic request validation checks: * Request logging includes remote address and user agent * Log initial requests before any middleware/body parsing * Include method for checking if request is valid at a low-level (IE checking request verb) * Include source-specific methods for checking if request payload is valid --- src/common/infrastructure/Atomic.ts | 6 +++ src/index.ts | 36 ++++++++++--- src/sources/JellyfinSource.ts | 6 --- src/sources/PlexSource.ts | 10 ++-- .../ingressNotifiers/IngressNotifier.ts | 52 +++++++++++++++++++ .../ingressNotifiers/JellyfinNotifier.ts | 35 +++++++++++++ src/sources/ingressNotifiers/PlexNotifier.ts | 42 +++++++++++++++ .../ingressNotifiers/TautulliNotifier.ts | 36 +++++++++++++ src/utils.ts | 17 +++++- 9 files changed, 220 insertions(+), 20 deletions(-) create mode 100644 src/sources/ingressNotifiers/IngressNotifier.ts create mode 100644 src/sources/ingressNotifiers/JellyfinNotifier.ts create mode 100644 src/sources/ingressNotifiers/PlexNotifier.ts create mode 100644 src/sources/ingressNotifiers/TautulliNotifier.ts diff --git a/src/common/infrastructure/Atomic.ts b/src/common/infrastructure/Atomic.ts index 5ef03042..3ee7e85a 100644 --- a/src/common/infrastructure/Atomic.ts +++ b/src/common/infrastructure/Atomic.ts @@ -128,3 +128,9 @@ export interface MalojaScrobbleV2RequestData extends MalojaScrobbleRequestData { export interface MalojaScrobbleV3RequestData extends MalojaScrobbleRequestData { artists: string[] } + +export interface RemoteIdentityParts { + host: string, + proxy: string | undefined, + agent: string | undefined +} diff --git a/src/index.ts b/src/index.ts index c41e80ff..fcec2db5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,6 +36,9 @@ import DeezerSource from "./sources/DeezerSource.js"; import AbstractSource from "./sources/AbstractSource.js"; import {PlayObject, TrackStringOptions} from "./common/infrastructure/Atomic.js"; import SpotifySource from "./sources/SpotifySource.js"; +import {JellyfinNotifier} from "./sources/ingressNotifiers/JellyfinNotifier.js"; +import {PlexNotifier} from "./sources/ingressNotifiers/PlexNotifier.js"; +import {TautulliNotifier} from "./sources/ingressNotifiers/TautulliNotifier.js"; dayjs.extend(utc) @@ -245,7 +248,10 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) }); }) + const tauIngress = new TautulliNotifier(); app.postAsync('/tautulli', async function(this: any, req, res) { + tauIngress.trackIngress(req, false); + const payload = TautulliSource.formatPlayObj(req.body, true); // try to get config name from payload if (req.body.scrobblerConfig !== undefined) { @@ -276,13 +282,19 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) const plexMiddle = plexRequestMiddle(); const plexLog = createLabelledLogger('plexReq', 'Plex Request'); - app.postAsync('/plex', plexMiddle, async function (req, res) { - const { payload } = req as any; - if(payload === undefined) { - - plexLog.warn('Received a request without any data'); + const plexIngress = new PlexNotifier(); + app.postAsync('/plex', + async function (req, res, next) { + // track request before parsing body to ensure we at least log that something is happening + // (in the event body parsing does not work or request is not POST/PATCH) + plexIngress.trackIngress(req, true); + next(); + }, + plexMiddle, async function (req, res) { + plexIngress.trackIngress(req, false); - } else { + const { payload } = req as any; + if(payload !== undefined) { const playObj = PlexSource.formatPlayObj(payload, true); const pSources = scrobbleSources.getByType('plex') as PlexSource[]; @@ -300,7 +312,17 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`) // webhook plugin sends json with context type text/utf-8 so we need to parse it differently const jellyfinJsonParser = bodyParser.json({type: 'text/*'}); - app.postAsync('/jellyfin', jellyfinJsonParser, async function (req, res) { + const jellyIngress = new JellyfinNotifier(); + app.postAsync('/jellyfin', + async function (req, res, next) { + // track request before parsing body to ensure we at least log that something is happening + // (in the event body parsing does not work or request is not POST/PATCH) + jellyIngress.trackIngress(req, true); + next(); + }, + jellyfinJsonParser, async function (req, res) { + jellyIngress.trackIngress(req, false); + const playObj = JellyfinSource.formatPlayObj(req.body, true); const pSources = scrobbleSources.getByType('jellyfin') as JellyfinSource[]; for (const source of pSources) { diff --git a/src/sources/JellyfinSource.ts b/src/sources/JellyfinSource.ts index e29fece0..d974f93e 100644 --- a/src/sources/JellyfinSource.ts +++ b/src/sources/JellyfinSource.ts @@ -9,8 +9,6 @@ export default class JellyfinSource extends MemorySource { users; servers; - seenServers = {}; - declare config: JellySourceConfig; constructor(name: any, config: JellySourceConfig, internal: InternalConfig) { @@ -144,10 +142,6 @@ export default class JellyfinSource extends MemorySource { } handle = async (playObj: any, allClients: any) => { - if(this.seenServers[playObj.meta.server] === undefined) { - this.seenServers[playObj.meta.server] = playObj.meta.sourceVersion; - this.logger.info(`Received data from server ${playObj.meta.server} (Version ${playObj.meta.sourceVersion}) for the first time.`); - } if (!this.isValidEvent(playObj)) { return; } diff --git a/src/sources/PlexSource.ts b/src/sources/PlexSource.ts index e4d43ed6..75ccce78 100644 --- a/src/sources/PlexSource.ts +++ b/src/sources/PlexSource.ts @@ -224,13 +224,11 @@ export const plexRequestMiddle = () => { } let validFile = null; - for (const namedFiles of Object.values(files)) { + for (const namedFile of Object.values(files)) { // @ts-expect-error TS(2571): Object is of type 'unknown'. - for(const file of namedFiles) { - if (file.mimetype.includes('json')) { - validFile = file; - break; - } + if (namedFile.mimetype.includes('json')) { + validFile = namedFile; + break; } } if (validFile === null) { diff --git a/src/sources/ingressNotifiers/IngressNotifier.ts b/src/sources/ingressNotifiers/IngressNotifier.ts new file mode 100644 index 00000000..0453b85e --- /dev/null +++ b/src/sources/ingressNotifiers/IngressNotifier.ts @@ -0,0 +1,52 @@ +import {Logger} from "winston"; +import {createLabelledLogger, remoteHostIdentifiers, remoteHostStr} from "../../utils.js"; +import {Request} from "express"; +import {RemoteIdentityParts} from "../../common/infrastructure/Atomic.js"; + + +export abstract class IngressNotifier { + + identifier: string; + logger: Logger; + + remotes: Record = {}; + + protected constructor(name: string) { + this.identifier = name; + const label = `Ingress - ${name}`; + this.logger = createLabelledLogger(label, label); + } + + public trackIngress(req: Request, isRaw: boolean) { + const parts = remoteHostIdentifiers(req); + const id = `${parts.host}-${parts.proxy ?? ''}`; + + const notifs: string[] = []; + + const newRemote = this.remotes[id] === undefined; + if (newRemote) { + this.remotes[id] = parts; + notifs.push(`Received request from a new remote address: ${remoteHostStr(req)}`); + } + const requestNotif = this.notifyByRequest(req, isRaw); + if (requestNotif !== undefined) { + notifs.push(`${!newRemote ? remoteHostStr(req) : ''} ${requestNotif}`); + } + const [sourceValid, sourceNotif] = this.notifyBySource(req, isRaw); + if (sourceNotif !== undefined) { + notifs.push(`${!newRemote && requestNotif === undefined ? `${remoteHostStr(req)} ` : ''}${sourceNotif}`); + } + + if (requestNotif !== undefined || sourceValid === false) { + this.logger.warn(notifs.join(' | ')); + } else { + this.logger.info(notifs.join(' | ')) + } + } + + notifyByRequest(req: Request, isRaw: boolean): string | undefined { + return; + } + + abstract notifyBySource(req: Request, isRaw: boolean): [boolean, string | undefined]; +} diff --git a/src/sources/ingressNotifiers/JellyfinNotifier.ts b/src/sources/ingressNotifiers/JellyfinNotifier.ts new file mode 100644 index 00000000..20fdd5bc --- /dev/null +++ b/src/sources/ingressNotifiers/JellyfinNotifier.ts @@ -0,0 +1,35 @@ +import {IngressNotifier} from "./IngressNotifier.js"; +import {Request} from "express"; +import JellyfinSource from "../JellyfinSource.js"; + +export class JellyfinNotifier extends IngressNotifier { + + constructor() { + super('Jellyfin'); + } + + seenServers: Record = {}; + notifyBySource(req: Request, isRaw: boolean): [boolean, (string | undefined)] { + if(!isRaw) { + const playObj = JellyfinSource.formatPlayObj(req.body); + + if(playObj.meta.server === undefined) { + return [false, `Payload from Jellyfin did not contain server info! Make sure 'Send all Properties' is checked in webhook`]; + } + + if(this.seenServers[playObj.meta.server] === undefined) { + this.seenServers[playObj.meta.server] = playObj.meta.sourceVersion; + return [true, `Received valid data from server ${playObj.meta.server} (Version ${playObj.meta.sourceVersion}) for the first time.`]; + } + } + return [true, undefined]; + } + + notifyByRequest(req: Request, isRaw: boolean): string | undefined { + if(req.method !== 'POST') { + return `Expected POST request (webhook payload) but received ${req.method}`; + } + return; + } + +} diff --git a/src/sources/ingressNotifiers/PlexNotifier.ts b/src/sources/ingressNotifiers/PlexNotifier.ts new file mode 100644 index 00000000..0fc583be --- /dev/null +++ b/src/sources/ingressNotifiers/PlexNotifier.ts @@ -0,0 +1,42 @@ +import {IngressNotifier} from "./IngressNotifier.js"; +import {Request} from "express"; +import PlexSource from "../PlexSource.js"; + +export class PlexNotifier extends IngressNotifier { + + constructor() { + super('Plex'); + } + + seenServers: string[] = []; + notifyBySource(req: Request, isRaw: boolean): [boolean, (string | undefined)] { + if(!isRaw) { + const { payload } = req as any; + if(payload === undefined) { + + return [false, 'Received a request without any data']; + + } + + const playObj = PlexSource.formatPlayObj(payload, true); + + if(playObj.meta.server === undefined) { + return [false, `Payload from Plex did not contain server info! Check Plex logs for any errors.`]; + } + + if(!this.seenServers.includes(playObj.meta.server)) { + this.seenServers.push(playObj.meta.server); + return [true, `Received valid data from server ${playObj.meta.server} for the first time.`]; + } + } + return [true, undefined]; + } + + notifyByRequest(req: Request, isRaw: boolean): string | undefined { + if(req.method !== 'POST') { + return `Expected POST request (webhook payload) but received ${req.method}`; + } + return; + } + +} diff --git a/src/sources/ingressNotifiers/TautulliNotifier.ts b/src/sources/ingressNotifiers/TautulliNotifier.ts new file mode 100644 index 00000000..e7d6d0dd --- /dev/null +++ b/src/sources/ingressNotifiers/TautulliNotifier.ts @@ -0,0 +1,36 @@ +import {IngressNotifier} from "./IngressNotifier.js"; +import {Request} from "express"; +import PlexSource from "../PlexSource.js"; +import TautulliSource from "../TautulliSource.js"; + +export class TautulliNotifier extends IngressNotifier { + + constructor() { + super('Tautulli'); + } + + seenServers: string[] = []; + notifyBySource(req: Request, isRaw: boolean): [boolean, (string | undefined)] { + if(!isRaw) { + const playObj = TautulliSource.formatPlayObj(req, true); + + if(!this.seenServers.includes(playObj.meta.server)) { + this.seenServers.push(playObj.meta.server); + let msg = [`Received valid data from server ${playObj.meta.server} for the first time.`]; + if(playObj.meta.library === undefined) { + msg.push('WARNING: library was not defined in payload. If you want to filter plays by library this must be present in webhook payload.'); + } + return [true, msg.join(' ')]; + } + } + return [true, undefined]; + } + + notifyByRequest(req: Request, isRaw: boolean): string | undefined { + if(req.method !== 'POST') { + return `Expected POST request (webhook payload) but received ${req.method}`; + } + return; + } + +} diff --git a/src/utils.ts b/src/utils.ts index c131f243..54c78ac0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,7 +7,8 @@ import JSON5 from 'json5'; import { TimeoutError, WebapiError } from "spotify-web-api-node/src/response-error.js"; import { Response } from 'superagent'; import Ajv, {Schema} from 'ajv'; -import {PlayObject, TrackStringOptions} from "./common/infrastructure/Atomic.js"; +import {PlayObject, RemoteIdentityParts, TrackStringOptions} from "./common/infrastructure/Atomic.js"; +import {Request} from "express"; const {format} = winston; const {combine, printf, timestamp, label, splat, errors} = format; @@ -449,3 +450,17 @@ export const validateJson = (config: object, schema: Schema, logger: Logger): throw new Error('Config schema validity failure'); } } + +export const remoteHostIdentifiers = (req: Request): RemoteIdentityParts => { + const remote = req.connection.remoteAddress; + const proxyRemote = Array.isArray(req.headers["x-forwarded-for"]) ? req.headers["x-forwarded-for"][0] : req.headers["x-forwarded-for"]; + const ua = req.headers["user-agent"]; + + return {host: remote, proxy: proxyRemote, agent: ua}; +} + +export const remoteHostStr = (req: Request): string => { + const {host, proxy, agent} = remoteHostIdentifiers(req); + + return `${host}${proxy !== undefined ? ` (${proxy})` : ''}${agent !== undefined ? ` (UA: ${agent})` : ''}`; +} -- 2.51.2 From 865f1064f52a1c96e6d460210d36cd8b08c89b8e Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 22 Feb 2023 11:43:57 -0500 Subject: [PATCH 61/64] docs: Add FAQ --- README.md | 4 ++ docs/FAQ.md | 87 +++++++++++++++++++++++++++++++++++++++++++ docs/configuration.md | 2 + 3 files changed, 93 insertions(+) create mode 100644 docs/FAQ.md diff --git a/README.md b/README.md index abca0da7..e9975895 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,10 @@ Output is also provided to stdout/stderr as well as file if specified in configu On first startup you may need to authorize Spotify and/or Last.fm by visiting the callback URL (which can also be accessed from the status page). Visit the status page above to find the applicable link to trigger this. +## Help/FAQ + +Having issues with connections or configuration? Check the [FAQ](/docs/FAQ.md) before creating an issue! + ## License MIT diff --git a/docs/FAQ.md b/docs/FAQ.md new file mode 100644 index 00000000..66435e86 --- /dev/null +++ b/docs/FAQ.md @@ -0,0 +1,87 @@ +# Connection Issues + +## Plex/Tautulli/Jellyfin don't work + +These three [sources](/README.md#source) are **ingress-based** which means that multi-scrobbler waits for the Plex/Tautulli/Jellyfin server to contact multi-scrobbler, as opposed to multi-scrobbler contacting the server. + +multi-scrobbler will log information about any server that connects to it for these three services. In the logs it looks something like this: + +``` +2023-02-22T10:55:56-05:00 info : [Ingress - Plex ] Received request from a new remote address: ::ffff:192.168.0.140 (UA: PlexMediaServer/1.24.5.5173-8dcc73a59) +2023-02-22T10:55:56-05:00 info : [Ingress - Plex ] ::ffff:192.168.0.140 (UA: PlexMediaServer/1.24.5.5173-8dcc73a59) Received valid data from server examplePlex for the first time. +2023-02-22T10:55:56-05:00 warn : [Plex Request ] Received valid Plex webhook payload but no Plex sources are configured +``` +It also logs if a server tries to connect to a URL that it does not recognize: + +``` +2023-02-22T11:16:12-05:00 debug : [App ] Server received POST request from ::ffff:192.168.0.140 (UA: PlexMediaServer/1.24.5.5173-8dcc73a59) to unknown route: /plkex +``` +**So, if you do not see either of these in your logs then Plex/Tautulli/Jellyfin is not able to connect to your multi-scrobbler instance at all.** + +This is not something multi-scrobbler can fix and means you have an issue in your network. + +### Troubleshooting + +Check or try all these steps before submitting an issue: + +#### Turn on Debug Logging + +First, turn on **debug** logging for multi-scrobbler by setting the environmental variable `LOG_LEVEL=debug`: + +* using node `LOG_LEVEL=debug ... node src/index.js` +* using docker `docker run -e LOG_LEVEL=debug ... foxxmd/multi-scrobbler` + +Check the output for any additional information. + +#### Check Host name and URL + +The URLs examples in the [configuration](/docs/configuration.md) documentation assume you are running Plex/Tautulli/Jellyfin on the same server as multi-scrobbler. If these are not the same machine then you need to determine the IP address or domain name that multi-scrobbler is reachable at and use that instead of `localhost` when configuring these sources. **This is likely the same host name that you would use to access the web interface for multi-scrobbler.** + +EX `http://localhost:9078/plex` -> `http://192.168.0.140:9078/plex` + +#### Check Firewall and Port Forwarding + +If the machine multi-scrobbler is running on has a firewall ensure that port **9078** is open. Or if it is in another network entirely make sure your router is forwarding this port and it is open to the correct machine. + +#### Check Source Service Logs + +Plex/Tautulli/Jellyfin all have logs that will log if they cannot connect to multi-scrobbler. Check these for further information. + +##### Plex + +Settings -> Manage -> Console + +##### Tautulli + +Check the command-line output of the application or docker logs. + +##### Jellyfin + +Administration -> Dashboard -> Advanced -> Logs + +## Spotify/Deezer/LastFM won't authenticate + +Ensure any **client id** or **secrets** are correct in your configuration. + +The callback/redirect URL for these services must be: + +* the same address you would use to access the multi-scrobbler web interface + * the web-interface must be accessible from the browser you are completing authentication from. + +If multi-scrobbler is not running on the same machine your browser is on then the default/example addresses (`http://localhost...`) **will not work.** You must determine the address you can reach the web interface at (such as `http://192.168.0.140:9078`) then use that in place of `localhost` in the callback URLs. + +EX `http://localhost:9078/lastfm/callback` -> `http://192.168.0.220:9078/lastfm/callback` + +# Configuration Issues + +## Config could not be parsed + +If you see something like this in your logs: + +``` +2023-02-19T10:05:42-06:00 warn : [App] App config file exists but could not be parsed! +2023-02-19T10:05:42-06:00 error : [App] Exited with uncaught error +2023-02-19T10:05:42-06:00 error : [App] Error: config.json could not be parsed +``` + +It means the JSON in your configuration file is not valid. Copy and paste your configuration into a site like [JSONLine](https://jsonlint.com/) to find out where errors you have and fix them. diff --git a/docs/configuration.md b/docs/configuration.md index d1f81936..acbbddf6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -39,6 +39,8 @@ **MS will parse configuration from both configuration types.** You can mix and match configurations but it is generally better to stick to one or the other. +TIP: Check the [**FAQ**](/docs/FAQ.md) if you have any issues after configuration! + ## ENV-Based Configuration This is done by passing environmental variables and so does not require any files to run MS. -- 2.51.2 From e08a1ad51451566f7a20c286b5e44db8c728499c Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 22 Feb 2023 11:46:01 -0500 Subject: [PATCH 62/64] fix: fix typo --- docs/FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 66435e86..5cb683b3 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -84,4 +84,4 @@ If you see something like this in your logs: 2023-02-19T10:05:42-06:00 error : [App] Error: config.json could not be parsed ``` -It means the JSON in your configuration file is not valid. Copy and paste your configuration into a site like [JSONLine](https://jsonlint.com/) to find out where errors you have and fix them. +It means the JSON in your configuration file is not valid. Copy and paste your configuration into a site like [JSONLint](https://jsonlint.com/) to find out where errors you have and fix them. -- 2.51.2 From 02dee5fb6c0a217792eac0272c613e73a65d3e3b Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 22 Feb 2023 11:54:13 -0500 Subject: [PATCH 63/64] docs: Add bug report template --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..e8ecae14 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Logs** +If possible reproduce the issue with [debug logging ON](/docs/FAQ.md#turn-on-debug-logging) + +``` +Copy and paste as much log data as possible related to this issue here. +``` + +**Versions (please complete the following information):** +Provide version information for any related sources/clients. + +- multi-scrobbler: [e.g. 0.4.0 on docker] +- maloja [e.g. 3.1.4] +- jellyfin [e.g. 10.8.9] + +**Additional context** +Add any other context about the problem here. -- 2.51.2 From 19687b725dbfe9c176dce7899587c659e569b2b5 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 22 Feb 2023 12:00:13 -0500 Subject: [PATCH 64/64] fix: Fix logging empty ingress notifications --- src/sources/ingressNotifiers/IngressNotifier.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sources/ingressNotifiers/IngressNotifier.ts b/src/sources/ingressNotifiers/IngressNotifier.ts index 0453b85e..b9652f36 100644 --- a/src/sources/ingressNotifiers/IngressNotifier.ts +++ b/src/sources/ingressNotifiers/IngressNotifier.ts @@ -37,10 +37,12 @@ export abstract class IngressNotifier { notifs.push(`${!newRemote && requestNotif === undefined ? `${remoteHostStr(req)} ` : ''}${sourceNotif}`); } - if (requestNotif !== undefined || sourceValid === false) { - this.logger.warn(notifs.join(' | ')); - } else { - this.logger.info(notifs.join(' | ')) + if(notifs.length > 0) { + if (requestNotif !== undefined || sourceValid === false) { + this.logger.warn(notifs.join(' | ')); + } else { + this.logger.info(notifs.join(' | ')) + } } }