From f58015933b8662439c34491db1a8eec246a1ec27 Mon Sep 17 00:00:00 2001 From: Wietse Wind Date: Fri, 6 Oct 2023 00:58:05 +0200 Subject: [PATCH] Beta, add webserver (socket, event, dirlisting), tx ordering, etc. --- README.md | 32 +- bin/webserver.mjs | 92 +++ index.mjs | 15 +- lib/events/ledgerReady.mjs | 91 +++ lib/onLedger.mjs | 53 +- lib/onTransaction.mjs | 115 ++- lib/onValidation.mjs | 5 + xpop/xpopV1.mjs => lib/xpop/v1.mjs | 0 xpop/generateV1.mjs => lib/xpopGenerate.mjs | 6 +- package-lock.json | 788 +++++++++++++++++++- package.json | 10 +- 11 files changed, 1144 insertions(+), 63 deletions(-) create mode 100644 bin/webserver.mjs create mode 100644 lib/events/ledgerReady.mjs rename xpop/xpopV1.mjs => lib/xpop/v1.mjs (100%) rename xpop/generateV1.mjs => lib/xpopGenerate.mjs (97%) diff --git a/README.md b/README.md index 4b1c342..b450bef 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,10 @@ Based on the work by @RichardAH: https://github.com/RichardAH/xpop-generator This tool creates a folder structore in the `./store` directory, where it creates sub-directories like this: -> `store / {networkid} / {ledgerno(0, -6)} / {ledgerno(-6, -3)} / {ledgerno(-3)} /` +> `store / {networkid} / {ledgerpath///} /` + +The `ledgerpath` is the ledger index chunked from right to left in sections of three digits, making sure there +are max. 1000 subfolders per level. This allows for easy dir listing & cleaning. So e.g. for NetworkId `21338`, ledger index `82906790`, the path would be: @@ -37,3 +40,30 @@ Every folder will contain the following files: `npm run dev` to launch (verbose) `npm run xpopgen` to launch, less verbose +## Webserver + +This script also runs a webserver is the env. var is provided for the TCP port & URL Prefix where the app will run: + +```bash +EVENT_SOCKET_PORT="3000" +URL_PREFIX="https://4849bf891e06.ngrok.app" +``` + +#### WebSocket + +You can listen for xPOP publish events (live, so you don't hve to poll). + +By default you will get all xPOP events. If you want to filter on a specific address, provide +the r-address in the URL path. If you also want to receive the xPOP Blob, also provide `/blob` in the URL path. + +E.g. `/blob/rwietsevLFg8XSmG3bEZzFein1g8RBqWDZ` would listen for xPOPs for account `rwietsevLFg8XSmG3bEZzFein1g8RBqWDZ` +and serve the (hex encoded) xPOP in the `xpop.blob` property. + +#### HTTP File Browser + +On the HTTP port a file listing is also provided & xPOPs can be downloaded at `/xpop/{tx hash}`. + +Original source files to reconstruct the xPOP locally can be downloaded at `/{networkid}/`. + +This file browser is for development and test purposes only, for production, put a static webserver +in front of this application & reverse proxy only the WebSocket (HTTP Upgrade) server. diff --git a/bin/webserver.mjs b/bin/webserver.mjs new file mode 100644 index 0000000..916be19 --- /dev/null +++ b/bin/webserver.mjs @@ -0,0 +1,92 @@ +import { WebSocket } from 'ws' +import morgan from 'morgan' +import express from 'express' +import expressWs from 'express-ws' +import serveIndex from 'serve-index' +import 'dotenv/config' + +let wss // WebSocket Server + +if (!wss) { + if (process.env?.EVENT_SOCKET_PORT && process.env?.URL_PREFIX) { + const port = Number(process.env.EVENT_SOCKET_PORT) + + try { + const app = express() + app.enable('trust proxy') + app.use(morgan('combined', { })) + + wss = expressWs(app) + + // app.use(function middlware (req, res, next) { + // return next() + // }) + + app.use('/', express.static('./store/')) + + app.use('/:networkId([0-9]{1,})', (req, res, next) => { + return serveIndex('./store/' + req.params.networkId + '/', { icons: false })(req, res, next) + }) + + // app.get('/', function route (req, res, next){ + // console.log('get route', req.testing) + // res.end() + // }) + + app.ws('*', function wsclient (ws, req) { + Object.assign(ws, { req, }) + ws.on('message', function wsmsg (msg) { + // Ignore + // console.log(msg) + }) + }) + + app.listen(port) + console.log('Started Event Socket Service at TCP port', port) + } catch (e) { + console.log('Cannot start Webserver & Event Socket Service at port', port, e) + } + } else { + console.log('Not starting Webserver & Event Socket Service, EVENT_SOCKET_PORT and/or URL_PREFIX unset') + } +} + +const emit = _data => { + if (wss) { + wss.getWss().clients.forEach(function each (client) { + const data = Object.assign({}, { ..._data }) + // Needed to prevent shared object manipulation with multiple clients + data.xpop = Object.assign({}, _data.xpop) + + if (client.readyState === WebSocket.OPEN) { + // console.log(client) + // console.log(client?._xpopAccount) + // console.log(client?._xpopBlob) + + let account = '' + const accountAddress = client.req.url.match(/r[a-zA-Z0-9]{18,}/) + const blob = !!client.req.url.match(/\/blob/i) + + if (accountAddress) { + account = accountAddress[0] + } + + if (!blob && data?.xpop?.blob) { + data.xpop.blob = undefined + } + + if (account === '' || data.account === account) { + client.send(JSON.stringify(data), { binary: false }) + } + } + }) + + return wss.getWss().clients.length + } + + return false +} + +export { + emit, +} diff --git a/index.mjs b/index.mjs index 92395be..7bfebfa 100644 --- a/index.mjs +++ b/index.mjs @@ -2,13 +2,14 @@ import { XrplClient } from 'xrpl-client' import { createDirectory } from './lib/createDirectory.mjs' import { onValidation } from './lib/onValidation.mjs' import { onLedger } from './lib/onLedger.mjs' -import { onTransaction } from './lib/onTransaction.mjs' import 'dotenv/config' import assert from 'assert' +import './bin/webserver.mjs' assert(process.env?.NODES, 'ENV var missing: NODES, containing: a comma separated list of websocket endpoints') await createDirectory('store') +await createDirectory('store/xpop') process.env.NODES.split(',').map(h => h.trim()) .map(h => new XrplClient(h)).map(async c => { @@ -22,8 +23,9 @@ process.env.NODES.split(',').map(h => h.trim()) c.send({ command: "subscribe", streams: [ "validations", "ledger", - "transactions", - "transactions_proposed" + // No transactions, to make it easier for clients transactions are + // processed in order (sorted on sequence) and emitted in order + // to clients to prevent async tx sequence problems. ] }) c.on("validation", validation => onValidation({ @@ -38,11 +40,4 @@ process.env.NODES.split(',').map(h => h.trim()) ledger, connection: c, })) - - c.on("transaction", transaction => onTransaction({ - connectionUrl: c.getState()?.server?.uri, - networkId: c.getState()?.server?.networkId, - transaction, - connection: c, - })) }) diff --git a/lib/events/ledgerReady.mjs b/lib/events/ledgerReady.mjs new file mode 100644 index 0000000..30cf8eb --- /dev/null +++ b/lib/events/ledgerReady.mjs @@ -0,0 +1,91 @@ +const ledgers = {} + +const externalResolvablePromise = () => { + let _resolve + const meta = { + resolved: false, + } + const promise = new Promise(resolve => { + _resolve = (r) => { + meta.resolved = true + return resolve(r) + } + }) + + return { promise, resolve: _resolve, meta, } +} + +/** + * + * @param {number} ledger - Ledger Index + * @param {(ledger_binary_transactions|ledger_info|vl|validation)} readyElement - + */ + +const ledgerReady = async (ledger, readyElement) => { + // console.log('LedgerReady', ledger, readyElement) + const ledgerIndexString = String(ledger) + + if (!ledgers?.[ledgerIndexString]) { + const ledger_binary_transactions = externalResolvablePromise() + const ledger_info = externalResolvablePromise() + const vl = externalResolvablePromise() + + const ready = Promise.all([ + ledger_binary_transactions.promise, + ledger_info.promise, + vl.promise, + ]) + + Object.assign(ledgers, { + [ledgerIndexString]: { + ledger_binary_transactions, + ledger_info, + vl, + validation: 0, + ready, + } + }) + + // Set timeout to clean up + setTimeout(() => { + // console.log('Cleaning up', ledgerIndexString) + if (ledgers?.[ledgerIndexString]) { + ledgers?.[ledgerIndexString]?.ledger_binary_transactions?.resolve(false) + ledgers?.[ledgerIndexString]?.ledger_info?.resolve(false) + ledgers?.[ledgerIndexString]?.vl?.resolve(false) + } + + // Force GC + setTimeout(() => { + if (ledgers?.[ledgerIndexString]) delete ledgers?.[ledgerIndexString] + }, 50) + }, 20_000) + } + + if ( + readyElement === 'ledger_binary_transactions' + || readyElement === 'ledger_info' + || readyElement === 'vl' + ) { + ledgers[ledgerIndexString][readyElement].resolve(new Date() / 1000) + } + if (readyElement === 'validation') { + ledgers[ledgerIndexString][readyElement]++ + } +} + +const waitForLedgerReady = ledgerIndex => { + return ledgers?.[String(ledgerIndex)]?.ready +} + +const isLedgerReady = ledgerIndex => { + return ledgers?.[String(ledgerIndex)]?.ledger_binary_transactions.meta.resolved + && ledgers?.[String(ledgerIndex)]?.ledger_info.meta.resolved + && ledgers?.[String(ledgerIndex)]?.vl.meta.resolved +} + +export { + ledgerReady, + isLedgerReady, + waitForLedgerReady, +} diff --git a/lib/onLedger.mjs b/lib/onLedger.mjs index 7381133..5d464e7 100644 --- a/lib/onLedger.mjs +++ b/lib/onLedger.mjs @@ -2,6 +2,8 @@ import { writeFile } from 'fs' import { ledgerIndexToFolders } from './ledgerIndexToFolders.mjs' import { computeBinaryTransactionHash } from './computeBinaryTransactionHash.mjs' import { dirExists } from './dirExists.mjs' +import { ledgerReady, waitForLedgerReady } from './events/ledgerReady.mjs' +import { onTransaction } from './onTransaction.mjs' import 'dotenv/config' const obtainedHumanReadableLedgers = [] @@ -18,7 +20,7 @@ const onLedger = async ({ const storeDir = new URL('../' + relativeStoreDir, import.meta.url).pathname if (await dirExists(storeDir)) { - ;[ + const ledgerData = [ ...( obtainedBinaryTxLedgers.indexOf(ledger.ledger_index) < 0 ? [ @@ -37,6 +39,9 @@ const onLedger = async ({ connection.send({ command: 'ledger', ledger_index: ledger.ledger_index, + transactions: true, + expand: true, + binary: false, }) ] : []), @@ -71,6 +76,8 @@ const onLedger = async ({ writeFile(storeDir + '/ledger_binary_transactions.json', Buffer.from(JSON.stringify(results.ledger), 'utf8'), err => { if (err) { console.log('Error writing file @ ' + storeDir) + } else { + ledgerReady(results.ledger_index, 'ledger_binary_transactions') } }) } @@ -87,16 +94,54 @@ const onLedger = async ({ console.log('Obtained ledger (JSON object)', relativeStoreDir, results.ledger_index, 'Hash', results.ledger.ledger_hash) - writeFile(storeDir + '/ledger_info.json', Buffer.from(JSON.stringify(results.ledger), 'utf8'), err => { + writeFile(storeDir + '/ledger_info.json', Buffer.from(JSON.stringify({ ...results.ledger, transactions: undefined, }), 'utf8'), err => { if (err) { console.log('Error writing file @ ' + storeDir) + } else { + ledgerReady(ledger.ledger_index, 'ledger_info') } }) + } } - })) - } + return results.ledger + })) + + /** + * Deal with transactions & fire events + */ + waitForLedgerReady(ledger.ledger_index).then(async () => { + if (ledgerData.length > 0) { + const [binary, json] = await Promise.all(ledgerData) + const sequetiallyMappedLedgerTxEvents = (json?.transactions || []).map(tx => { + return { + validated: true, + ledger_index: ledger.ledger_index, + transaction: tx, + } + }) + .sort((a, b) => a.transaction.Sequence - b.transaction.Sequence) + .reduce((promiseChain, current) => { + return promiseChain.then(() => { + // console.log(' » Tx events: Processing', current.transaction.Sequence) + return onTransaction({ + networkId, + transaction: current, + connection, + }) + }).then(() => { + // console.log(' » Tx events: Done ', current.transaction.Sequence) + }) + }, Promise.resolve()) + + sequetiallyMappedLedgerTxEvents.then(() => { + // console.log(' « « « « All transactions in ledger processed', ledger.ledger_index) + }); + } + }) + + } } } } diff --git a/lib/onTransaction.mjs b/lib/onTransaction.mjs index 4069f1d..99cea37 100644 --- a/lib/onTransaction.mjs +++ b/lib/onTransaction.mjs @@ -1,9 +1,12 @@ import { writeFile } from 'fs' import { dirExists } from './dirExists.mjs' import { ledgerIndexToFolders } from './ledgerIndexToFolders.mjs' -import { generateV1 as xpop } from '../xpop/generateV1.mjs' +import { xpopGenerate } from './xpopGenerate.mjs' +import { waitForLedgerReady } from './events/ledgerReady.mjs' +import { emit } from '../bin/webserver.mjs' import 'dotenv/config' +const xpopBinaryDir = new URL('../store/xpop', import.meta.url).pathname const lastSeenTransactions = [] const fields = (process.env?.FIELDSREQUIRED || '') @@ -21,52 +24,94 @@ const onTransaction = async ({ networkId, transaction, }) => { - if (transaction?.validated) { - const { transaction: tx } = transaction + if (transaction?.validated) { + const { transaction: tx } = transaction + + if (tx.hash && lastSeenTransactions.indexOf(tx.hash) < 0) { + lastSeenTransactions.unshift(tx.hash) + lastSeenTransactions.length = 3000 + + const validTx = hasRequiredFields(tx) + if (!process.env?.NOELIGIBLEFULLTXLOG) { + console.log('TX', tx.hash, validTx) + } + + if (validTx && transaction?.ledger_index) { + const relativeStorDir = 'store/' + networkId + '/' + ledgerIndexToFolders(transaction.ledger_index) + const storeDir = new URL('../' + relativeStorDir, import.meta.url).pathname + + console.log('xPOP eligible', relativeStorDir, process.env?.NOELIGIBLEFULLTXLOG ? tx.hash : tx) + + if (await dirExists(storeDir)) { + const wroteTxFile = await new Promise(resolve => { + writeFile(storeDir + '/tx_' + tx.hash + '.json', Buffer.from(JSON.stringify(transaction), 'utf8'), err => { + if (err) { + console.log('Error writing file @ ' + storeDir) + resolve(false) + } + resolve(true) + }) + }) - if (tx.hash && lastSeenTransactions.indexOf(tx.hash) < 0) { - lastSeenTransactions.unshift(tx.hash) - lastSeenTransactions.length = 3000 - - const validTx = hasRequiredFields(tx) - if (!process.env?.NOELIGIBLEFULLTXLOG) { - console.log('TX', tx.hash, validTx) - } - - if (validTx && transaction?.ledger_index) { - const relativeStorDir = 'store/' + networkId + '/' + ledgerIndexToFolders(transaction.ledger_index) - const storeDir = new URL('../' + relativeStorDir, import.meta.url).pathname - - console.log('xPOP eligible', relativeStorDir, process.env?.NOELIGIBLEFULLTXLOG ? tx.hash : tx) - - if (await dirExists(storeDir)) { - writeFile(storeDir + '/tx_' + tx.hash + '.json', Buffer.from(JSON.stringify(transaction), 'utf8'), err => { - if (err) { - console.log('Error writing file @ ' + storeDir) - } else { + if (wroteTxFile) { + await waitForLedgerReady(transaction.ledger_index) /** * TX all ready, written to filesystem, ... * This is where we start a slight delay to give the `onLedger` * routine some time to fetch & store and then we'll try to * generate an xPOP. */ - setTimeout(async () => { - await xpop({ - ledgerIndex: transaction.ledger_index, - networkId, - txHash: tx.hash, + const xpopBinary = await xpopGenerate({ + ledgerIndex: transaction.ledger_index, + networkId, + txHash: tx.hash, + }) + if (await dirExists(xpopBinaryDir)) { + const xpopWritten = await new Promise(resolve => { + writeFile(xpopBinaryDir + '/' + tx.hash, Buffer.from(xpopBinary, 'utf8'), err => { + if (err) { + console.log('Error writing binary XPOP', err) + resolve(false) + } else { + console.log('Wrote binary xPOP: ' + xpopBinaryDir + '/' + tx.hash) + resolve(true) + } + }) }) - }, 500) - // ^^ To check: is this enough? If e.g. retrieving the ledger info - // would take longer this may not be enough. Best solution: - // make this await the ledger fetching calls. - // Dirty: extend to e.g. 2000. + if (xpopWritten) { + console.log(' ### EMIT XPOP READY FOR', tx?.Account, Number(tx.Sequence), tx.hash) + + return await emit({ + account: tx?.Account, + sequence: tx.Sequence, + origin: { + tx: tx.hash, + networkId: networkId, + ledgerIndex: transaction.ledger_index, + burn: tx?.Fee, + }, + destination: { + networkId: tx?.OperationLimit, + }, + ...( + process.env?.URL_PREFIX + ? { + xpop: { + binary: `${process.env.URL_PREFIX}/xpop/${tx.hash}`, + source: `${process.env.URL_PREFIX}/${networkId}/${ledgerIndexToFolders(transaction.ledger_index)}/`, + blob: xpopBinary, + } + } + : {} + ) + }) + } + } } - }) + } } } } - } } export { diff --git a/lib/onValidation.mjs b/lib/onValidation.mjs index b058cc7..cb26efb 100644 --- a/lib/onValidation.mjs +++ b/lib/onValidation.mjs @@ -3,6 +3,7 @@ import { createDirectory } from './createDirectory.mjs' import 'dotenv/config' import { unlData } from './unlData.mjs' import { ledgerIndexToFolders } from './ledgerIndexToFolders.mjs' +import { ledgerReady } from './events/ledgerReady.mjs' const lastSeenValidations = [] let lastCreatedLedgerDir @@ -34,6 +35,8 @@ const onValidation = async ({ const relativeStorDir = 'store/' + networkId + '/' + ledgerIndexToFolders(validation.ledger_index) const storeDir = new URL('../' + relativeStorDir, import.meta.url).pathname + ledgerReady(validation.ledger_index, 'validation') + if (lastCreatedLedgerDir !== validation.ledger_index) { await createDirectory(relativeStorDir) lastCreatedLedgerDir = validation.ledger_index @@ -41,6 +44,8 @@ const onValidation = async ({ writeFile(storeDir + '/vl.json', Buffer.from(JSON.stringify(unlData.data), 'utf8'), err => { if (err) { console.log('Error writing file @ ' + storeDir) + } else { + ledgerReady(validation.ledger_index, 'vl') } }) } diff --git a/xpop/xpopV1.mjs b/lib/xpop/v1.mjs similarity index 100% rename from xpop/xpopV1.mjs rename to lib/xpop/v1.mjs diff --git a/xpop/generateV1.mjs b/lib/xpopGenerate.mjs similarity index 97% rename from xpop/generateV1.mjs rename to lib/xpopGenerate.mjs index 06b62fc..96be0f1 100644 --- a/xpop/generateV1.mjs +++ b/lib/xpopGenerate.mjs @@ -1,5 +1,5 @@ import assert from 'assert' -import { xpop } from './xpopV1.mjs' +import { xpop } from './xpop/v1.mjs' import { writeFile, readFile, readdir } from 'fs' import { ledgerIndexToFolders } from '../lib/ledgerIndexToFolders.mjs' import { dirExists } from '../lib/dirExists.mjs' @@ -20,7 +20,7 @@ const catjson = async file => { return JSON.parse(buffer.toString()) } -const generateV1 = async ({ +const xpopGenerate = async ({ ledgerIndex, networkId, txHash @@ -83,5 +83,5 @@ const generateV1 = async ({ } export { - generateV1, + xpopGenerate, } diff --git a/package-lock.json b/package-lock.json index a7f35ab..78573cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,13 +13,21 @@ "dotenv": "^16.3.1", "ed25519": "^0.0.5", "elliptic": "^6.5.4", + "express-ws": "^5.0.2", + "morgan": "^1.10.0", "node-fetch": "^3.3.2", "ripple-address-codec": "^4.3.0", "ripple-binary-codec": "^1.10.0", + "serve-index": "^1.9.1", + "ws": "^8.14.2", "xrpl-client": "^2.2.0" }, "devDependencies": { "serve": "^14.2.1" + }, + "optionalDependencies": { + "bufferutil": "^4.0.7", + "utf-8-validate": "^6.0.3" } }, "node_modules/@zeit/schemas": { @@ -32,7 +40,6 @@ "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==", - "dev": true, "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -157,6 +164,12 @@ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true }, + "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==", + "peer": true + }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -213,6 +226,27 @@ } ] }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/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==" + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -234,6 +268,54 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, + "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==", + "peer": 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" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "peer": true + }, "node_modules/boxen": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", @@ -519,6 +601,30 @@ "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==", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "peer": true, + "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==", + "peer": true + }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -621,6 +727,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "peer": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/dotenv": { "version": "16.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", @@ -648,6 +772,11 @@ "nan": "^2.14.1" } }, + "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/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -668,6 +797,15 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "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==", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es5-ext": { "version": "0.10.62", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", @@ -701,6 +839,20 @@ "ext": "^1.1.2" } }, + "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==", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -724,6 +876,138 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "peer": 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" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-ws": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/express-ws/-/express-ws-5.0.2.tgz", + "integrity": "sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==", + "dependencies": { + "ws": "^7.4.6" + }, + "engines": { + "node": ">=4.5.0" + }, + "peerDependencies": { + "express": "^4.0.0 || ^5.0.0-alpha.1" + } + }, + "node_modules/express-ws/node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/express-ws/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/express/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==", + "peer": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "peer": true + }, + "node_modules/express/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==", + "peer": true + }, + "node_modules/express/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==", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/ext": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", @@ -779,6 +1063,39 @@ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "peer": 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" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "peer": true + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -798,6 +1115,24 @@ "node": ">=12.20.0" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -939,6 +1274,22 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "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==", + "peer": true, + "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/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -948,6 +1299,18 @@ "node": ">=10.17.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==", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -978,6 +1341,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "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==", + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -1134,17 +1506,52 @@ "safe-buffer": "^5.1.2" } }, + "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==", + "peer": true, + "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==", + "peer": true + }, "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/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "peer": true, + "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==", + "peer": true, + "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==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -1153,7 +1560,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -1201,6 +1607,45 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/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/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1215,7 +1660,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -1282,6 +1726,15 @@ "node": ">=8" } }, + "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==", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -1322,11 +1775,22 @@ "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==", + "peer": true, + "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==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -1346,6 +1810,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", @@ -1367,12 +1839,40 @@ "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==", "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==", + "peer": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "dev": true }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "peer": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -1382,6 +1882,30 @@ "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==", + "peer": true, + "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/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -1497,6 +2021,66 @@ } ] }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "peer": true + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "peer": 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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "peer": 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==", + "peer": true + }, + "node_modules/send/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==", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve": { "version": "14.2.1", "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.1.tgz", @@ -1559,6 +2143,97 @@ "node": ">= 0.6" } }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/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/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "peer": true, + "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/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "peer": true + }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -1592,12 +2267,35 @@ "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==", + "peer": true, + "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/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -1668,6 +2366,15 @@ "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==", + "peer": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -1685,6 +2392,19 @@ "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", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "peer": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -1693,6 +2413,15 @@ "is-typedarray": "^1.0.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==", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-check": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", @@ -1722,10 +2451,11 @@ } }, "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz", + "integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==", "hasInstallScript": true, + "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -1750,11 +2480,19 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "peer": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -1796,6 +2534,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/websocket/node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1861,6 +2611,26 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "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/xrpl-client": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xrpl-client/-/xrpl-client-2.2.0.tgz", diff --git a/package.json b/package.json index 99a4a16..98e213e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.mjs", "scripts": { "dev": "nodemon .", - "xpopdev": "source .env && nodemon --max-old-space-size=50 .", + "xpopdev": "source .env && nodemon --max-old-space-size=40 .", "serve": "serve ./store/" }, "author": "Wietse Wind ", @@ -20,12 +20,20 @@ "dotenv": "^16.3.1", "ed25519": "^0.0.5", "elliptic": "^6.5.4", + "express-ws": "^5.0.2", + "morgan": "^1.10.0", "node-fetch": "^3.3.2", "ripple-address-codec": "^4.3.0", "ripple-binary-codec": "^1.10.0", + "serve-index": "^1.9.1", + "ws": "^8.14.2", "xrpl-client": "^2.2.0" }, "devDependencies": { "serve": "^14.2.1" + }, + "optionalDependencies": { + "bufferutil": "^4.0.7", + "utf-8-validate": "^6.0.3" } }