From e0c21b32bdf95606f623e1eb43c8c78e38ea9414 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Tue, 14 Jan 2025 07:41:25 -0500 Subject: [PATCH 01/41] start of Swagger YAML file --- swagger.yaml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/swagger.yaml b/swagger.yaml index 1c2eb22f..a352cebe 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -1,11 +1,19 @@ -swagger: '2.0' +swagger: "2.0" info: - version: '1.0.0' - title: 'E-Commerce API' - description: 'API for managing brands, products, and user cart' -host: 'localhost:3000' + version: "1.0.0" + title: "Sunglasses" + description: "API for e-commerce sunglasses store managing brands, products, and user cart" +host: "localhost:3000" schemes: - - 'http' -basePath: '/api' + - "http" +basePath: "/api" produces: - - 'application/json' + - "application/json" +paths: + /products: + tags: + - "products" + summary: "returns all products" + description: "" + produces: + - "application/json" From 09fcb64c630144d48c2fd6a8764d26b58c576244 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 10 Mar 2025 15:24:30 -0400 Subject: [PATCH 02/41] update launch config for tests and update packages --- .vscode/launch.json | 40 +- app/server.js | 9 +- package-lock.json | 1148 ++++++++++++++++++++++++------------------- package.json | 2 +- 4 files changed, 670 insertions(+), 529 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 5e70b293..7ba5d1be 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,14 +1,28 @@ { - // Use IntelliSense to learn about possible Node.js debug attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Sunglasses.io", - "program": "${workspaceRoot}/app/server.js" - } - ] -} \ No newline at end of file + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Run Mocha Tests", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "-u", + "bdd", + "--timeout", + "999999", + "--colors", + "${workspaceFolder}/test/server.test.js" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal" + }, + { + "type": "node", + "request": "launch", + "name": "Run Server", + "program": "${workspaceFolder}/app/server.js", + "cwd": "${workspaceFolder}" + } + ] +} diff --git a/app/server.js b/app/server.js index 5201d84d..cb44241b 100644 --- a/app/server.js +++ b/app/server.js @@ -23,9 +23,12 @@ app.use((err, req, res, next) => { app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); // Starting the server -const PORT = process.env.PORT || 3000; -app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); +if (require.main === module) { + const PORT = process.env.PORT || 3000; + app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); }); +} + module.exports = app; diff --git a/package-lock.json b/package-lock.json index ab397dea..769a5979 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "jsonwebtoken": "^9.0.2", "querystring": "^0.2.0", "rand-token": "^0.4.0", - "router": "^1.3.2", + "router": "^2.1.0", "swagger-ui-express": "^5.0.0", "yamljs": "^0.3.0" }, @@ -70,10 +70,11 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "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, + "license": "MIT", "engines": { "node": ">=6" } @@ -121,11 +122,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/array-flatten": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", - "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==" - }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -162,9 +158,10 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -174,7 +171,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -189,17 +186,19 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -224,14 +223,30 @@ "node": ">= 0.8" } }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -286,21 +301,6 @@ "node": ">=10" } }, - "node_modules/chai-http/node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -452,9 +452,10 @@ } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -502,19 +503,6 @@ "node": ">=6" } }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -552,14 +540,29 @@ } }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -580,13 +583,44 @@ "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -617,41 +651,43 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "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==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -660,6 +696,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/array-flatten": { @@ -667,43 +707,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "node_modules/express/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/express/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/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -711,10 +714,11 @@ "dev": true }, "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==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -723,12 +727,13 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -805,6 +810,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -832,6 +838,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -855,19 +862,42 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dependencies": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -920,11 +950,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -939,32 +970,11 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -973,9 +983,10 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -1122,6 +1133,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -1135,6 +1147,12 @@ "node": ">=8" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -1290,6 +1308,15 @@ "node": ">=10" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1299,9 +1326,13 @@ } }, "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==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/methods": { "version": "1.1.2", @@ -1343,10 +1374,11 @@ } }, "node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1355,32 +1387,32 @@ } }, "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", @@ -1388,19 +1420,16 @@ }, "engines": { "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" } }, "node_modules/mocha/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1411,11 +1440,26 @@ } } }, - "node_modules/mocha/node_modules/debug/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/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", @@ -1428,18 +1472,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1458,9 +1490,13 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1540,9 +1576,10 @@ } }, "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==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, "node_modules/pathval": { "version": "1.1.1", @@ -1578,11 +1615,12 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -1613,6 +1651,7 @@ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } @@ -1621,6 +1660,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1661,20 +1701,26 @@ } }, "node_modules/router": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/router/-/router-1.3.8.tgz", - "integrity": "sha512-461UFH44NtSfIlS83PUg2N7OZo86BC/kB3dY77gJdsODsBhhw7+2uE0tzTINxrY9CahCUVk1VhpWCA5i1yoIEg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz", + "integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==", + "license": "MIT", "dependencies": { - "array-flatten": "3.0.0", - "debug": "2.6.9", - "methods": "~1.1.2", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "setprototypeof": "1.2.0", - "utils-merge": "1.0.1" + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 18" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" } }, "node_modules/safe-buffer": { @@ -1716,9 +1762,10 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -1738,10 +1785,20 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -1752,59 +1809,106 @@ "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==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.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==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/set-function-length": { + "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", - "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.1", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.2", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "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/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1944,6 +2048,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -2011,10 +2116,11 @@ } }, "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", @@ -2092,10 +2198,11 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -2170,9 +2277,9 @@ } }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "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 }, "ansi-regex": { @@ -2206,11 +2313,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "array-flatten": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", - "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==" - }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -2241,9 +2343,9 @@ "dev": true }, "body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -2253,7 +2355,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -2269,12 +2371,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-stdout": { @@ -2293,14 +2395,22 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, - "call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "requires": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" } }, "camelcase": { @@ -2338,17 +2448,6 @@ "methods": "^1.1.2", "qs": "^6.11.2", "superagent": "^8.0.9" - }, - "dependencies": { - "qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - } } }, "chalk": { @@ -2463,9 +2562,9 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" }, "cookie-signature": { "version": "1.0.6", @@ -2501,16 +2600,6 @@ "type-detect": "^4.0.0" } }, - "define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "requires": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2538,11 +2627,21 @@ } }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2563,9 +2662,27 @@ "dev": true }, "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } }, "escalade": { "version": "3.1.1", @@ -2590,36 +2707,36 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -2631,36 +2748,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "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==", - "requires": { - "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" - } - }, - "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==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } } } }, @@ -2671,21 +2758,21 @@ "dev": true }, "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==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" } }, "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -2772,14 +2859,29 @@ "dev": true }, "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "requires": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" } }, "glob": { @@ -2824,12 +2926,9 @@ } }, "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { - "get-intrinsic": "^1.1.3" - } + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" }, "has-flag": { "version": "4.0.0", @@ -2837,28 +2936,15 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "requires": { - "get-intrinsic": "^1.2.2" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" - }, "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" }, "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "requires": { "function-bind": "^1.1.2" } @@ -2976,6 +3062,11 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -3105,15 +3196,20 @@ "yallist": "^4.0.0" } }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, "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==" }, "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" }, "methods": { "version": "1.1.2", @@ -3140,58 +3236,62 @@ } }, "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "dependencies": { "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "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 - } + "ms": "^2.1.3" + } + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" } }, "ms": { @@ -3207,12 +3307,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -3225,9 +3319,9 @@ "dev": true }, "object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" }, "on-finished": { "version": "2.4.1", @@ -3280,9 +3374,9 @@ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "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==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "pathval": { "version": "1.1.1", @@ -3306,11 +3400,11 @@ } }, "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" } }, "querystring": { @@ -3364,17 +3458,20 @@ "dev": true }, "router": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/router/-/router-1.3.8.tgz", - "integrity": "sha512-461UFH44NtSfIlS83PUg2N7OZo86BC/kB3dY77gJdsODsBhhw7+2uE0tzTINxrY9CahCUVk1VhpWCA5i1yoIEg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz", + "integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==", "requires": { - "array-flatten": "3.0.0", - "debug": "2.6.9", - "methods": "~1.1.2", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "setprototypeof": "1.2.0", - "utils-merge": "1.0.1" + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "dependencies": { + "path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==" + } } }, "safe-buffer": { @@ -3396,9 +3493,9 @@ } }, "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "requires": { "debug": "2.6.9", "depd": "2.0.0", @@ -3415,6 +3512,11 @@ "statuses": "2.0.1" }, "dependencies": { + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -3428,35 +3530,23 @@ } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" } }, "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==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "requires": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "set-function-length": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", - "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", - "requires": { - "define-data-property": "^1.1.1", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.2", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "send": "0.19.0" } }, "setprototypeof": { @@ -3465,13 +3555,47 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" } }, "sprintf-js": { @@ -3618,9 +3742,9 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "wrap-ansi": { @@ -3685,9 +3809,9 @@ } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, "yargs-unparser": { diff --git a/package.json b/package.json index afe392cf..8df87fd7 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "jsonwebtoken": "^9.0.2", "querystring": "^0.2.0", "rand-token": "^0.4.0", - "router": "^1.3.2", + "router": "^2.1.0", "swagger-ui-express": "^5.0.0", "yamljs": "^0.3.0" }, From 49e605b3820dbf7c2e3439417906aa784640ae87 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 10 Mar 2025 15:27:08 -0400 Subject: [PATCH 03/41] GET-brands: add test --- test/server.test.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/server.test.js b/test/server.test.js index 7ff14c8f..f8900cd9 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -7,7 +7,18 @@ chai.use(chaiHttp); // TODO: Write tests for the server -describe('Brands', () => {}); +describe('/GET brands', () => { + it('should return an array of all brands', (done) => { + chai + .request(server) + .get('/brands') + .end((err, res) => { + res.should.have.status(200); + res.should.be.an('array'); + done(); + }); + }); +}); describe('Login', () => {}); From f82fa0127f3190b64e5825717ff92b1728d369f8 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 10 Mar 2025 15:36:22 -0400 Subject: [PATCH 04/41] GET-brands: implement route --- app/server.js | 4 ++++ test/server.test.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/server.js b/app/server.js index cb44241b..ece87c1c 100644 --- a/app/server.js +++ b/app/server.js @@ -19,6 +19,10 @@ app.use((err, req, res, next) => { res.status(500).send('Something broke!'); }); +app.get('/brands', (req, res) => { + res.status(200).json(brands); +}); + // Swagger app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); diff --git a/test/server.test.js b/test/server.test.js index f8900cd9..e97e1dd1 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -14,7 +14,7 @@ describe('/GET brands', () => { .get('/brands') .end((err, res) => { res.should.have.status(200); - res.should.be.an('array'); + res.body.should.be.an('array'); done(); }); }); From 11f2f573404bded3f3bd53b1dcf46f684adfb896 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Tue, 11 Mar 2025 12:13:36 -0400 Subject: [PATCH 05/41] GET-products: add tests --- test/server.test.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/server.test.js b/test/server.test.js index e97e1dd1..c4f48da9 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -17,6 +17,19 @@ describe('/GET brands', () => { res.body.should.be.an('array'); done(); }); + }); +}); + +describe('/GET products', () => { + it('should return an array of ALL products from all brands', (done) => { + chai + .request(server) + .get('/products') + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an('array'); + done(); + }); }); }); From 799486467b923308b42ee4ceeba7a4cd411a8d81 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Tue, 11 Mar 2025 12:19:51 -0400 Subject: [PATCH 06/41] GET-products: implement route --- app/server.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/server.js b/app/server.js index ece87c1c..96889e6a 100644 --- a/app/server.js +++ b/app/server.js @@ -23,6 +23,10 @@ app.get('/brands', (req, res) => { res.status(200).json(brands); }); +app.get('/products', (req, res) => { + res.status(200).json(products); +}); + // Swagger app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); From a07bf45745eae7038019fe96336133858e9a5077 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Tue, 11 Mar 2025 13:21:58 -0400 Subject: [PATCH 07/41] GET-brands-id-products: add tests --- test/server.test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/server.test.js b/test/server.test.js index e97e1dd1..cd9ea98b 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -20,6 +20,23 @@ describe('/GET brands', () => { }); }); +describe('/GET brands/:id/products', () => { + it('should return array of products for a given brand', (done) => { + const targetId = brands[0].id; + + chai + .request(server) + .get(`brands/${targetId}/products`) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an('array'); + res.body.every(product => product.id === targetId).should.be.true; + done(); + }); + + }); +}); + describe('Login', () => {}); describe('Cart', () => {}); From 6bbe15f4ff03f27775b7b6f55bb3d9653aad6793 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Thu, 13 Mar 2025 08:50:42 -0400 Subject: [PATCH 08/41] GET-brands-id-products: add test --- test/server.test.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/server.test.js b/test/server.test.js index cd9ea98b..60f7ba19 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -22,15 +22,16 @@ describe('/GET brands', () => { describe('/GET brands/:id/products', () => { it('should return array of products for a given brand', (done) => { - const targetId = brands[0].id; - + + const brandId = 1; + chai .request(server) - .get(`brands/${targetId}/products`) + .get(`/brands/${brandId}/products`) .end((err, res) => { res.should.have.status(200); res.body.should.be.an('array'); - res.body.every(product => product.id === targetId).should.be.true; + res.body.every(product => product.categoryId == brandId).should.be.true; done(); }); From ce180b5479a9c6c74409e29e8ef9fc7fca247a08 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Thu, 13 Mar 2025 10:50:18 -0400 Subject: [PATCH 09/41] GET-brands-id-products: implement route --- app/server.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/server.js b/app/server.js index ece87c1c..f838c33d 100644 --- a/app/server.js +++ b/app/server.js @@ -23,6 +23,12 @@ app.get('/brands', (req, res) => { res.status(200).json(brands); }); +app.get('/brands/:id/products', (req, res) => { + const brandId = req.params.id; + const selectedProducts = products.filter(product => product.categoryId == brandId); + res.status(200).json(selectedProducts); +}); + // Swagger app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); From 8dc4f6fd8fbceddbd31a95b353b3fcab85c29291 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Fri, 21 Mar 2025 10:57:06 -0400 Subject: [PATCH 10/41] POST-login: add tests --- test/server.test.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/server.test.js b/test/server.test.js index e97e1dd1..9d41120d 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -7,7 +7,7 @@ chai.use(chaiHttp); // TODO: Write tests for the server -describe('/GET brands', () => { +describe('GET /brands', () => { it('should return an array of all brands', (done) => { chai .request(server) @@ -20,6 +20,20 @@ describe('/GET brands', () => { }); }); -describe('Login', () => {}); +describe('POST /login', () => { + it('should login user and return token', (done) => { + const loginInfo = { username: 'yellowleopard753', password: 'jonjon' }; + chai + .request(server) + .post('/login') + .send(loginInfo) + .end((err, res) => { + res.should.have(200); + res.body.should.be.an('object'); + res.body.should.have.property('token'); + done(); + }); + }); +}); describe('Cart', () => {}); From 8de4af148b3e8299db343b1ce59e95903e2830df Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 24 Mar 2025 09:34:47 -0400 Subject: [PATCH 11/41] POST-login: implement route --- app/server.js | 12 ++++++++++++ test/server.test.js | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/server.js b/app/server.js index ece87c1c..051b7fe3 100644 --- a/app/server.js +++ b/app/server.js @@ -6,6 +6,8 @@ const YAML = require('yamljs'); const swaggerDocument = YAML.load('./swagger.yaml'); // Replace './swagger.yaml' with the path to your Swagger file const app = express(); +const PRIVATE_KEY = "ILoveSunglasses"; + app.use(bodyParser.json()); // Importing the data from JSON files @@ -23,6 +25,16 @@ app.get('/brands', (req, res) => { res.status(200).json(brands); }); +app.post('/login', (req, res) => { + const { username, password } = req.body; + const user = users.find(user => user.login.username === username && user.login.password === password); + if (!user) { + return res.status(401).json({ error: 'Invalid login info' }); + } + const token = jwt.sign({ username: username }, PRIVATE_KEY, { expiresIn: '1h' }); + res.status(200).json({token}); +}); + // Swagger app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); diff --git a/test/server.test.js b/test/server.test.js index 9d41120d..4ae7a7b8 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -28,7 +28,7 @@ describe('POST /login', () => { .post('/login') .send(loginInfo) .end((err, res) => { - res.should.have(200); + res.should.have.status(200); res.body.should.be.an('object'); res.body.should.have.property('token'); done(); From 12a34fa4b116b7b2372c3e8d1d316b44805800c9 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Tue, 25 Mar 2025 07:40:45 -0400 Subject: [PATCH 12/41] POST-login: add test for invalid login info --- test/server.test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/server.test.js b/test/server.test.js index 4ae7a7b8..e21426a1 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -34,6 +34,22 @@ describe('POST /login', () => { done(); }); }); + it('should return 401 code for invalid login', (done) => { + const loginInfo = { + username: 'RandomGuy', + password: 'qwerty' + }; + chai + .request(server) + .post('/login') + .send(loginInfo) + .end((err, res) => { + res.should.have.status(401); + res.body.should.be.an('object'); + res.body.should.have.property('error').eql('Invalid login info'); + done(); + }); + }); }); describe('Cart', () => {}); From af8f372a0ce211623d032184c0279f2bf193065b Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Sat, 29 Mar 2025 09:58:30 -0400 Subject: [PATCH 13/41] implement verifyToken middleware --- app/server.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/server.js b/app/server.js index 051b7fe3..36c21cb6 100644 --- a/app/server.js +++ b/app/server.js @@ -15,6 +15,21 @@ const users = require('../initial-data/users.json'); const brands = require('../initial-data/brands.json'); const products = require('../initial-data/products.json'); +const verifyToken = (req, res, next) => { + const authHeader = req.headers.authorization; + if (!authHeader) { + return res.status(401).json({ error: 'No token provided' }); + } + const token = authHeader.split(' ')[1]; + try { + const decoded = jwt.verify(token, PRIVATE_KEY); + req.username = decoded.username; + next(); + } catch (error) { + res.status(401).json({ error: 'Token invalid or expired' }); + } +}; + // Error handling app.use((err, req, res, next) => { console.error(err.stack); From ecb720b73922b6970ce239045c035d0a2ba54a17 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 31 Mar 2025 08:26:59 -0400 Subject: [PATCH 14/41] add temporary test and route to test middleware --- app/server.js | 4 ++++ test/server.test.js | 53 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/app/server.js b/app/server.js index 36c21cb6..22abdc1d 100644 --- a/app/server.js +++ b/app/server.js @@ -36,6 +36,10 @@ app.use((err, req, res, next) => { res.status(500).send('Something broke!'); }); +app.get('/test-auth', verifyToken, (req, res) => { + res.status(200).json({ message: 'Authenticated', username: req.username }); +}); + app.get('/brands', (req, res) => { res.status(200).json(brands); }); diff --git a/test/server.test.js b/test/server.test.js index e21426a1..7a6226d1 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -22,7 +22,7 @@ describe('GET /brands', () => { describe('POST /login', () => { it('should login user and return token', (done) => { - const loginInfo = { username: 'yellowleopard753', password: 'jonjon' }; + const loginInfo = { username: 'lazywolf342', password: 'tucker' }; chai .request(server) .post('/login') @@ -53,3 +53,54 @@ describe('POST /login', () => { }); describe('Cart', () => {}); + +describe('Auth Middleware', () => { + let token; + + before((done) => { + chai + .request(server) + .post('/login') + .send({ username: 'lazywolf342', password: 'tucker'}) + .end((err, res) => { + token = res.body.token; + done(); + }); + }); + + it('should allow access with a valid token', (done) => { + chai + .request(server) + .get('/test-auth') + .set('Authorization', `Bearer ${token}`) + .end((err, res) => { + res.should.have.status(200); + res.body.should.have.property('message').eql('Authenticated'); + res.body.should.have.property('username').eql('lazywolf342'); + done(); + }); + }); + + it('should return 401 with no token', (done) => { + chai + .request(server) + .get('/test-auth') + .end((err, res) => { + res.should.have.status(401); + res.body.should.have.property('error').eql('No token provided'); + done(); + }); + }); + + it('should return 401 with an invalid token', (done) => { + chai + .request(server) + .get('/test-auth') + .set('Authorization', 'Bearer invalidtoken123') + .end((err, res) => { + res.should.have.status(401); + res.body.should.have.property('error').eql('Token invalid or expired'); + done(); + }); + }); +}); From 3e601b0d5e2efc888cf39e56c5297edc5dd06843 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 31 Mar 2025 08:42:44 -0400 Subject: [PATCH 15/41] remove temporary tests and route --- app/server.js | 4 ---- test/server.test.js | 50 --------------------------------------------- 2 files changed, 54 deletions(-) diff --git a/app/server.js b/app/server.js index 22abdc1d..36c21cb6 100644 --- a/app/server.js +++ b/app/server.js @@ -36,10 +36,6 @@ app.use((err, req, res, next) => { res.status(500).send('Something broke!'); }); -app.get('/test-auth', verifyToken, (req, res) => { - res.status(200).json({ message: 'Authenticated', username: req.username }); -}); - app.get('/brands', (req, res) => { res.status(200).json(brands); }); diff --git a/test/server.test.js b/test/server.test.js index 7a6226d1..fc072847 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -54,53 +54,3 @@ describe('POST /login', () => { describe('Cart', () => {}); -describe('Auth Middleware', () => { - let token; - - before((done) => { - chai - .request(server) - .post('/login') - .send({ username: 'lazywolf342', password: 'tucker'}) - .end((err, res) => { - token = res.body.token; - done(); - }); - }); - - it('should allow access with a valid token', (done) => { - chai - .request(server) - .get('/test-auth') - .set('Authorization', `Bearer ${token}`) - .end((err, res) => { - res.should.have.status(200); - res.body.should.have.property('message').eql('Authenticated'); - res.body.should.have.property('username').eql('lazywolf342'); - done(); - }); - }); - - it('should return 401 with no token', (done) => { - chai - .request(server) - .get('/test-auth') - .end((err, res) => { - res.should.have.status(401); - res.body.should.have.property('error').eql('No token provided'); - done(); - }); - }); - - it('should return 401 with an invalid token', (done) => { - chai - .request(server) - .get('/test-auth') - .set('Authorization', 'Bearer invalidtoken123') - .end((err, res) => { - res.should.have.status(401); - res.body.should.have.property('error').eql('Token invalid or expired'); - done(); - }); - }); -}); From 425c202ca51263319bbe2a0869106581c8ea6579 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 31 Mar 2025 12:05:22 -0400 Subject: [PATCH 16/41] GET /me/cart: add test --- test/server.test.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/test/server.test.js b/test/server.test.js index fc072847..3f8169db 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -52,5 +52,31 @@ describe('POST /login', () => { }); }); -describe('Cart', () => {}); +describe('GET /me/cart', () => { + let token; + + before((done) => { + chai + .request(server) + .post('/login') + .send({ username: 'yellowleopard753', password: 'jonjon' }) + .end((err, res) => { + token = res.body.token; + done(); + }); + }); + + it('should return cart for authenticated user', (done) => { + chai + .request(server) + .get('/me/cart') + .set('Authorization', `Bearer ${token}`) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an('array'); + done(); + }); + }); + +}); From 98036a8a4d71899c4d25778f6d348302574976dd Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 31 Mar 2025 15:46:34 -0400 Subject: [PATCH 17/41] GET /me/cart: implement route --- app/server.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/server.js b/app/server.js index 36c21cb6..1f25f15b 100644 --- a/app/server.js +++ b/app/server.js @@ -50,6 +50,14 @@ app.post('/login', (req, res) => { res.status(200).json({token}); }); +app.get('/me/cart', verifyToken, (req, res) => { + const currentUser = users.find(user => user.login.username === req.username); + if (!currentUser) { + return res.status(404).json({ error: 'User not found' }); + } + res.status(200).json(currentUser.cart); +}); + // Swagger app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); From 2d827b2c8ef13956b70be54dc9cc3bb37bea1326 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Tue, 1 Apr 2025 11:38:35 -0400 Subject: [PATCH 18/41] POST /me/cart: add tests --- test/server.test.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/server.test.js b/test/server.test.js index 3f8169db..15999eb6 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -80,3 +80,34 @@ describe('GET /me/cart', () => { }); +describe('POST /me/cart', () => { + let token; + let addToCart; + + before((done) => { + chai + .request(server) + .post('/login') + .send({ username: 'yellowleopard753', password: 'jonjon' }) + .end((err, res) => { + token = res.body.token; + done(); + }); + }); + + it('should add product to me/cart', (done) => { + addToCart = { productId: '1', quantity: 5 }; + + chai + .request(server) + .post('/me/cart') + .set('Authorization', `Bearer ${token}`) + .send(addToCart) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an('array'); + res.body.should.deep.include({ productId: '1', quantity: 5 }); + done(); + }); + }); +}); \ No newline at end of file From d22a543dd67009d3f23fa42f846b610306bacddb Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Wed, 2 Apr 2025 10:54:56 -0400 Subject: [PATCH 19/41] POST /me/cart: implement route --- app/server.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/server.js b/app/server.js index 1f25f15b..6c56c9af 100644 --- a/app/server.js +++ b/app/server.js @@ -58,6 +58,16 @@ app.get('/me/cart', verifyToken, (req, res) => { res.status(200).json(currentUser.cart); }); +app.post('/me/cart', verifyToken, (req, res) => { + const { productId, quantity } = req.body; + if (!productId || !quantity) { + return res.status(400).json({ error: 'Missing productId or quantity' }); + } + const currentUser = users.find(user => user.login.username === req.username); + currentUser.cart.push({ productId, quantity }); + res.status(200).json(currentUser.cart); +}); + // Swagger app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); From 28c0f2bf88a928911ed04e57ecf27a27a41d6966 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Thu, 3 Apr 2025 15:58:02 -0400 Subject: [PATCH 20/41] DELETE /me/cart/:productId -Add test --- test/server.test.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/server.test.js b/test/server.test.js index 15999eb6..c3651a27 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -110,4 +110,37 @@ describe('POST /me/cart', () => { done(); }); }); +}); + +describe ('DELETE /me/cart/:productId', () => { + let token; + before((done) => { + chai + .request(server) + .post('/login') + .send({ username: 'yellowleopard753', password: 'jonjon' }) + .end((err, res) => { + token = res.body.token; + chai + .request(server) + .post('/me/cart') + .set('Authorization', `Bearer ${token}`) + .send({ productId: '1', quantity: 5 }) + .end(() => done()); + }); + }); + + it('should delete the item from cart', (done) => { + chai + .request(server) + .delete('/me/cart/1') + .set('Authorization', `Bearer ${token}`) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an('array'); + res.body.should.not.deep.include({ productId: 1, quantity: 2 }); + done(); + }); + }); + }); \ No newline at end of file From f15909368384c2ac82e5b8b1a07bf51d4f4dad13 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Thu, 3 Apr 2025 16:05:31 -0400 Subject: [PATCH 21/41] DELETE /me/cart/:productId -Implement route --- app/server.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/server.js b/app/server.js index 6c56c9af..ff6cd70e 100644 --- a/app/server.js +++ b/app/server.js @@ -68,6 +68,13 @@ app.post('/me/cart', verifyToken, (req, res) => { res.status(200).json(currentUser.cart); }); +app.delete('/me/cart/:productId', verifyToken, (req, res) => { + const user = users.find(user => user.login.username === req.username); + const productId = req.params.productId; + user.cart = user.cart.filter(item => item.productId != productId); + res.status(200).json(user.cart); +}); + // Swagger app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); From 5ab96d6d808d1ed16522317c75947ee6a43f97f7 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Sat, 5 Apr 2025 12:09:48 -0400 Subject: [PATCH 22/41] PUT /me/cart/:productId - add tests --- test/server.test.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/server.test.js b/test/server.test.js index c3651a27..2b13fd51 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -143,4 +143,38 @@ describe ('DELETE /me/cart/:productId', () => { }); }); +}); + +describe ('PUT /me/cart/:productId', () => { + let token; + before((done) => { + chai + .request(server) + .post('/login') + .send({ username: 'yellowleopard753', password: 'jonjon' }) + .end((err, res) => { + token = res.body.token; + chai + .request(server) + .post('/me/cart') + .set('Authorization', `Bearer ${token}`) + .send({ productId: '1', quantity: 5 }) + .end(() => done()); + }); + }); + + it('should update quantity of given item in cart', (done) => { + chai + .request(server) + .put('/me/cart/1') + .set('Authorization', `Bearer ${token}`) + .send({ quantity: 10 }) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.an('array'); + res.body.should.deep.include({ productId: '1', quantity: 10 }); + done(); + }); + }); + }); \ No newline at end of file From 5d5be5167a46d57333201e54dc2a5107dd2b711b Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Sat, 5 Apr 2025 12:11:31 -0400 Subject: [PATCH 23/41] PUT /me/cart/:productId - implement route --- app/server.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/server.js b/app/server.js index ff6cd70e..f47206a5 100644 --- a/app/server.js +++ b/app/server.js @@ -75,6 +75,22 @@ app.delete('/me/cart/:productId', verifyToken, (req, res) => { res.status(200).json(user.cart); }); +app.put('/me/cart/:productId', verifyToken, (req, res) => { + const user = users.find(user => user.login.username === req.username); + const productId = req.params.productId; + const { quantity } = req.body; + if (!quantity || typeof quantity !== 'number') { + return res.status(400).json({ error: 'Must use valid quantity' }); + } + const item = user.cart.find(item => item.productId ===productId); + if (item) { + item.quantity = quantity; + res.status(200).json(user.cart); + } else { + res.status(404).json({ error: 'Item not found in cart' }); + } +}); + // Swagger app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); From 553f516d92faa409e20f4fe45d752499d1d87fbe Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Thu, 10 Apr 2025 22:05:47 -0400 Subject: [PATCH 24/41] merge 'GET-brands-id-products' into master --- app/server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/server.js b/app/server.js index 8c96b45f..a7abad19 100644 --- a/app/server.js +++ b/app/server.js @@ -52,6 +52,7 @@ app.post('/login', (req, res) => { } const token = jwt.sign({ username: username }, PRIVATE_KEY, { expiresIn: '1h' }); res.status(200).json({token}); +}); app.get('/brands/:id/products', (req, res) => { const brandId = req.params.id; From a86a5cb5a7451088db5bba3eb1695365368b2e08 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Tue, 3 Jun 2025 10:47:46 -0400 Subject: [PATCH 25/41] save edits to swagger file --- swagger.yaml | 253 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 242 insertions(+), 11 deletions(-) diff --git a/swagger.yaml b/swagger.yaml index a352cebe..e9b11f18 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -1,19 +1,250 @@ swagger: "2.0" info: version: "1.0.0" - title: "Sunglasses" - description: "API for e-commerce sunglasses store managing brands, products, and user cart" + title: "E-Commerce API" + description: "API for managing brands, products, and user cart" host: "localhost:3000" schemes: - "http" -basePath: "/api" -produces: - - "application/json" +tags: + - name: "products" + description: "Everything about the store's products" + - name: "login" + description: "User login" + - name: "cart" + description: "Access to user's cart" paths: + /brands: + get: + tags: + - "products" + summary: "All Brands" + description: "This endpoint returns an array of all brands offered in the store." + responses: + "200": + description: An array of brands + + /brands/{id}/products: + get: + tags: + - "products" + summary: "returns array of glasses by brand" + parameters: + - name: id + in: path + required: true + type: string + description: the ID represents the brand of glasses + responses: + "200": + description: An array of brands + /products: - tags: - - "products" - summary: "returns all products" - description: "" - produces: - - "application/json" + get: + tags: + - "products" + summary: "returns array of all products" + responses: + "200": + description: An array of products + + /login: + post: + tags: + - "login" + summary: "login user and access to cart" + parameters: + - in: body + name: login credentials + required: true + schema: + $ref: "#/definitions/Login" + responses: + "200": + description: "Login successful" + + /me/cart: + get: + tags: + - "cart" + summary: "returns contents of user's cart" + parameters: + - in: body + name: token + required: true + responses: + "200": + description: An array of the user's cart + + post: + tags: + - "cart" + summary: "add item to user's cart" + responses: + "200": + description: "Item added to cart" + + /me/cart/{productId}: + delete: + tags: + - "cart" + summary: "deletes an item from user's cart" + responses: + "200": + description: "Item deleted from cart" + + post: + tags: + - "cart" + summary: "changes quantity of an item in user's cart" + responses: + "200": + description: "Quantity of item in cart changed" + +definitions: + User: + type: object + properties: + gender: + type: string + example: "female" + cart: + type: array + items: + $ref: "#/definitions/CartItem" + name: + $ref: "#/definitions/Name" + location: + $ref: "#/definitions/Location" + email: + type: string + example: "susanna.richards@example.com" + login: + $ref: "#/definitions/Login" + dob: + type: string + example: "1954-10-09 10:47:17" + registered: + type: string + example: "2003-08-03 01:12:24" + phone: + type: string + example: "031-941-6700" + cell: + type: string + example: "081-032-7884" + picture: + $ref: "#/definitions/Picture" + nat: + type: string + example: "IE" + + Name: + type: object + properties: + title: + type: string + example: "mrs" + first: + type: string + example: "susanna" + last: + type: string + example: "richards" + + Location: + type: object + properties: + street: + type: string + example: "2343 herbert road" + city: + type: string + example: "duleek" + state: + type: string + example: "donegal" + postcode: + type: integer + example: 38567 + + Login: + type: object + properties: + username: + type: string + example: "yellowleopard753" + password: + type: string + example: "jonjon" + required: + - username + - password + + Token: + type: string + + Picture: + type: object + properties: + large: + type: string + example: "https://randomuser.me/api/portraits/women/55.jpg" + medium: + type: string + example: "https://randomuser.me/api/portraits/med/women/55.jpg" + thumbnail: + type: string + example: "https://ramdomuser.me/api/portraits/thumb/women/55.jpg" + + CartItem: + type: object + properties: + productId: + type: string + example: "2" + quantity: + type: integer + example: 5 + + Brand: + type: object + properties: + id: + type: string + description: Unique identifier representing a specific brand of sunglasses. + example: "1" + name: + type: string + description: A string containing the brand name + example: "Oakley" + + Product: + type: object + properties: + id: + type: string + description: Unique identifier representing a specific model of sunglasses + example: "3" + categoryId: + type: string + description: A string identifier that represents a specific brand of sunglasses + example: "1" + name: + type: string + description: Specifies the model of sunglasses + example: "Brown Sunglasses" + description: + type: string + description: A description of the specified sunglasses + example: "The best glasses in the world" + price: + type: integer + description: A number that represents the price of each pair of specified sunglasses + example: 50 + imageUrls: + type: array + description: An array of image URLs to display pics of specified product + items: + type: string + example: "https://image.shutterstock.com/z/stock-photo-yellow-sunglasses-white-backgound-600820286.jpg" From 2de28967159f8ec0014c8aea02fc83fccc06f9cb Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Wed, 4 Jun 2025 16:46:04 -0400 Subject: [PATCH 26/41] Add test to validate brand ID in GET /brands/:id/products --- test/server.test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/server.test.js b/test/server.test.js index 1cbc5879..a531ba50 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -49,6 +49,18 @@ describe('/GET brands/:id/products', () => { }); }); + + it('should return 404 if no products found for given brand id', (done) => { + chai.request(server) + .get('/brands/xyz/products') + .end((err, res) => { + res.should.have.status(404); + res.body.should.be.an('object'); + res.body.should.have.property('error').eql('No products found for this brand'); + done(); + }); + }); + }); describe('POST /login', () => { From 4b217b1cbdf023c3e0d8a896b7f7371cf0bb8157 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Wed, 4 Jun 2025 17:02:27 -0400 Subject: [PATCH 27/41] implement code to make test pass for brand ID validation in GET /brands/:id/products --- app/server.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/server.js b/app/server.js index 6e46b344..451d1bcb 100644 --- a/app/server.js +++ b/app/server.js @@ -57,6 +57,9 @@ app.post('/login', (req, res) => { app.get('/brands/:id/products', (req, res) => { const brandId = req.params.id; const selectedProducts = products.filter(product => product.categoryId == brandId); + if (selectedProducts.length === 0) { + return res.status(404).json({ error: 'No products found for this brand' }); + } res.status(200).json(selectedProducts); }); From 87b8b20d958cf06c4a42677dcde186ec56a9efe3 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Thu, 5 Jun 2025 14:16:44 -0400 Subject: [PATCH 28/41] update Swagger file with validation for GET /brands/:id/products --- swagger.yaml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/swagger.yaml b/swagger.yaml index e9b11f18..0c9501d2 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -28,7 +28,7 @@ paths: get: tags: - "products" - summary: "returns array of glasses by brand" + summary: "Get products for a specified brand" parameters: - name: id in: path @@ -37,7 +37,15 @@ paths: description: the ID represents the brand of glasses responses: "200": - description: An array of brands + description: An array of products by specified brand + schema: + type: array + items: + $ref: "#/definitions/Product" + "404": + description: No products found for specified brand + schema: + $ref: "#/definitions/ErrorResponse" /products: get: @@ -181,9 +189,6 @@ definitions: - username - password - Token: - type: string - Picture: type: object properties: @@ -197,6 +202,16 @@ definitions: type: string example: "https://ramdomuser.me/api/portraits/thumb/women/55.jpg" + Token: + type: string + + ErrorResponse: + type: object + properties: + error: + types: string + example: "No products found for specified brand" + CartItem: type: object properties: From c9fb93d70e03a443035a49b3842dd13d5029ea87 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 9 Jun 2025 10:47:38 -0400 Subject: [PATCH 29/41] update verifyToken middleware: if token is valid, extend exp by an hour --- app/server.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/server.js b/app/server.js index 451d1bcb..fcb2bcc3 100644 --- a/app/server.js +++ b/app/server.js @@ -24,6 +24,18 @@ const verifyToken = (req, res, next) => { try { const decoded = jwt.verify(token, PRIVATE_KEY); req.username = decoded.username; + + const payload = jwt.decode(token, { complete: true }).payload; + const currentExp = payload.exp; + + if (currentExp) { + const updatedExp = Math.floor(Date.now() / 1000) + 3600; + payload.exp = updatedExp; + + const updatedToken = jwt.sign(payload, PRIVATE_KEY); + res.locals.newToken = updatedToken; + } + next(); } catch (error) { res.status(401).json({ error: 'Token invalid or expired' }); @@ -68,7 +80,10 @@ app.get('/me/cart', verifyToken, (req, res) => { if (!currentUser) { return res.status(404).json({ error: 'User not found' }); } - res.status(200).json(currentUser.cart); + res.status(200).json({ + cart: currentUser.cart, + token: res.locals.newToken + }); }); app.post('/me/cart', verifyToken, (req, res) => { From d42c480219c2fc541705ce08fa2be68a58953925 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 9 Jun 2025 15:31:38 -0400 Subject: [PATCH 30/41] update protected routes to return the new, extended token with successful response --- app/server.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/server.js b/app/server.js index fcb2bcc3..0069ba97 100644 --- a/app/server.js +++ b/app/server.js @@ -93,14 +93,20 @@ app.post('/me/cart', verifyToken, (req, res) => { } const currentUser = users.find(user => user.login.username === req.username); currentUser.cart.push({ productId, quantity }); - res.status(200).json(currentUser.cart); + res.status(200).json({ + cart: currentUser.cart, + token: res.locals.newToken + }); }); app.delete('/me/cart/:productId', verifyToken, (req, res) => { const user = users.find(user => user.login.username === req.username); const productId = req.params.productId; user.cart = user.cart.filter(item => item.productId != productId); - res.status(200).json(user.cart); + res.status(200).json({ + cart: user.cart, + token: res.locals.newToken + }); }); app.put('/me/cart/:productId', verifyToken, (req, res) => { @@ -113,7 +119,10 @@ app.put('/me/cart/:productId', verifyToken, (req, res) => { const item = user.cart.find(item => item.productId ===productId); if (item) { item.quantity = quantity; - res.status(200).json(user.cart); + res.status(200).json({ + cart: user.cart, + token: res.locals.newToken + }); } else { res.status(404).json({ error: 'Item not found in cart' }); } From 5bb4ecf829d6762c4a9c056f19fd09999f4d92ea Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Wed, 11 Jun 2025 15:24:31 -0400 Subject: [PATCH 31/41] update swagger file --- swagger.yaml | 117 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 88 insertions(+), 29 deletions(-) diff --git a/swagger.yaml b/swagger.yaml index 0c9501d2..090dd3cd 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -18,17 +18,31 @@ paths: get: tags: - "products" - summary: "All Brands" - description: "This endpoint returns an array of all brands offered in the store." + summary: "all brands offered in the store" + produces: + - application/json responses: "200": description: An array of brands + /products: + get: + tags: + - "products" + summary: "returns array of all products" + produces: + - application/json + responses: + "200": + description: An array of products + /brands/{id}/products: get: tags: - "products" summary: "Get products for a specified brand" + produces: + - application/json parameters: - name: id in: path @@ -47,20 +61,13 @@ paths: schema: $ref: "#/definitions/ErrorResponse" - /products: - get: - tags: - - "products" - summary: "returns array of all products" - responses: - "200": - description: An array of products - /login: post: tags: - "login" summary: "login user and access to cart" + produces: + - application/json parameters: - in: body name: login credentials @@ -70,44 +77,88 @@ paths: responses: "200": description: "Login successful" + "401": + description: "Invalid login info" + schema: + $ref: "#/definitions/ErrorResponse" /me/cart: get: tags: - "cart" - summary: "returns contents of user's cart" + summary: "gets user's cart" + produces: + - application/json parameters: - - in: body - name: token + - in: header + name: tokenAuthorization + type: string required: true + description: "Bearer token" responses: "200": - description: An array of the user's cart - - post: - tags: - - "cart" - summary: "add item to user's cart" - responses: - "200": - description: "Item added to cart" + description: "user's cart and renewed token" + schema: + $ref: "#/definitions/CartResponse" + "404": + description: "User not found" + schema: + $ref: "#/definitions/ErrorResponse" /me/cart/{productId}: delete: tags: - "cart" summary: "deletes an item from user's cart" + produces: + - application/json + parameters: + - in: header + name: tokenAuthorization + type: string + required: true + description: "Bearer token" + - in: path + name: productId + type: string + required: true + description: "ID of product to delete" responses: "200": description: "Item deleted from cart" + schema: + $ref: "#/definitions/CartResponse" - post: + put: tags: - "cart" summary: "changes quantity of an item in user's cart" + produces: + - application/json + parameters: + - in: header + name: tokenAuthorization + type: string + required: true + description: "Bearer token" + - in: path + name: productId + type: string + required: true + description: "ID of product to change quantity of" responses: "200": - description: "Quantity of item in cart changed" + description: "Quantity changed in cart" + schema: + $ref: "#/definitions/CartResponse" + "400": + description: "Invalid quantity" + schema: + $ref: "#/definitions/ErrorResponse" + "404": + description: "Item not found" + schema: + $ref: "#/definitions/ErrorResponse" definitions: User: @@ -202,14 +253,11 @@ definitions: type: string example: "https://ramdomuser.me/api/portraits/thumb/women/55.jpg" - Token: - type: string - ErrorResponse: type: object properties: error: - types: string + type: string example: "No products found for specified brand" CartItem: @@ -222,6 +270,17 @@ definitions: type: integer example: 5 + CartResponse: + type: object + properties: + cart: + type: array + items: + $ref: "#/definitions/CartItem" + token: + type: string + example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InllbGxvd2xlb3BhcmQ3NTMiLCJpYXQiOjE3NDk0ODIxMzIsImV4cCI6MTc0OTQ4NTczMn0.vRtQc5EGzLtC9uGtfXQuR6-j-L-9MiVqLdMrKohxSL4" + Brand: type: object properties: From 22099f000841246f293127eb8496ab8eac87bed0 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Sat, 14 Jun 2025 09:11:08 -0400 Subject: [PATCH 32/41] more updates to swagger file: add post /me/cart route --- swagger.yaml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/swagger.yaml b/swagger.yaml index 090dd3cd..9d04f440 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -104,6 +104,35 @@ paths: description: "User not found" schema: $ref: "#/definitions/ErrorResponse" + post: + tags: + - "cart" + summary: "add item to user's cart" + consumes: + - application/json + produces: + - application/json + parameters: + - in: header + name: tokenAuthorization + type: string + required: true + description: "Bearer token" + - in: body + name: cartItem + required: true + description: "Item and quantity to add to cart" + schema: + $ref: "#/definitions/CartItem" + responses: + "200": + description: "Item added to cart" + schema: + $ref: "#/definitions/CartResponse" + "400": + description: "Missing id or quantity" + schema: + $ref: "#/definitions/ErrorResponse" /me/cart/{productId}: delete: @@ -262,12 +291,16 @@ definitions: CartItem: type: object + required: + - productId + - quantity properties: productId: type: string example: "2" quantity: type: integer + minimum: 1 example: 5 CartResponse: From 445f529459f9a2f29e7c2d756137ab93ff4dd3ad Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 23 Jun 2025 19:29:06 -0400 Subject: [PATCH 33/41] more swagger file updates --- swagger.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/swagger.yaml b/swagger.yaml index 9d04f440..1bb49639 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -91,7 +91,7 @@ paths: - application/json parameters: - in: header - name: tokenAuthorization + name: Authorization type: string required: true description: "Bearer token" @@ -114,7 +114,7 @@ paths: - application/json parameters: - in: header - name: tokenAuthorization + name: Authorization type: string required: true description: "Bearer token" @@ -143,7 +143,7 @@ paths: - application/json parameters: - in: header - name: tokenAuthorization + name: Authorization type: string required: true description: "Bearer token" @@ -166,7 +166,7 @@ paths: - application/json parameters: - in: header - name: tokenAuthorization + name: Authorization type: string required: true description: "Bearer token" From 5f9ba3b8e53706aadeb34ea83a1b53f75df73fff Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 23 Jun 2025 19:30:34 -0400 Subject: [PATCH 34/41] add CORS headers, allow localhost:3000 --- app/server.js | 9 +++++++++ package-lock.json | 37 +++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 47 insertions(+) diff --git a/app/server.js b/app/server.js index 0069ba97..d229ea67 100644 --- a/app/server.js +++ b/app/server.js @@ -4,10 +4,19 @@ const jwt = require('jsonwebtoken'); const swaggerUi = require('swagger-ui-express'); const YAML = require('yamljs'); const swaggerDocument = YAML.load('./swagger.yaml'); // Replace './swagger.yaml' with the path to your Swagger file +const cors = require('cors'); + const app = express(); const PRIVATE_KEY = "ILoveSunglasses"; +app.use(cors({ + origin: 'http://localhost:3000', + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Authorization', 'Content-Type'], + credentials: true +})); + app.use(bodyParser.json()); // Importing the data from JSON files diff --git a/package-lock.json b/package-lock.json index 769a5979..b045145c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "body-parser": "^1.18.2", + "cors": "^2.8.5", "express": "^4.18.2", "finalhandler": "latest", "http": "latest", @@ -471,6 +472,19 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, + "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==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1489,6 +1503,15 @@ "node": ">=0.10.0" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -2577,6 +2600,15 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, + "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": { + "object-assign": "^4", + "vary": "^1" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3318,6 +3350,11 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "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==" + }, "object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", diff --git a/package.json b/package.json index 8df87fd7..a1b4fe53 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "license": "ISC", "dependencies": { "body-parser": "^1.18.2", + "cors": "^2.8.5", "express": "^4.18.2", "finalhandler": "latest", "http": "latest", From 17807804c7c6566eca87615839f6b4e33cfd8deb Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Sat, 12 Jul 2025 10:58:24 -0400 Subject: [PATCH 35/41] Add validation for brand ID to GET /brands/:id/products --- app/server.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/server.js b/app/server.js index d229ea67..7ec1c4d9 100644 --- a/app/server.js +++ b/app/server.js @@ -77,11 +77,18 @@ app.post('/login', (req, res) => { app.get('/brands/:id/products', (req, res) => { const brandId = req.params.id; - const selectedProducts = products.filter(product => product.categoryId == brandId); - if (selectedProducts.length === 0) { - return res.status(404).json({ error: 'No products found for this brand' }); + const targetBrand = brands.filter(brand => brand.id === brandId); + if (targetBrand.length === 0) { + return res.status(404).json({ error: 'Invalid brand ID' }); } - res.status(200).json(selectedProducts); + const brandName = targetBrand[0].name; + const brandProducts = products.filter(product => product.categoryId === brandId) + if (brandProducts.length === 0) { + return res.status(422).json({ error: `No products found for ${brandName}` }); + } + + res.status(200).json(brandProducts); + }); app.get('/me/cart', verifyToken, (req, res) => { @@ -148,5 +155,4 @@ if (require.main === module) { }); } - -module.exports = app; +module.exports = app; \ No newline at end of file From a06ed25692c69dcd6d8078188019669206207436 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Sat, 12 Jul 2025 13:02:27 -0400 Subject: [PATCH 36/41] Update response from POST /login to return users cart with token --- app/server.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/server.js b/app/server.js index 7ec1c4d9..5440cbf8 100644 --- a/app/server.js +++ b/app/server.js @@ -72,7 +72,11 @@ app.post('/login', (req, res) => { return res.status(401).json({ error: 'Invalid login info' }); } const token = jwt.sign({ username: username }, PRIVATE_KEY, { expiresIn: '1h' }); - res.status(200).json({token}); + res.locals.newToken = token; + res.status(200).json({ + cart: user.cart, + token: res.locals.newToken + }); }); app.get('/brands/:id/products', (req, res) => { From 640d3b6edb3b77c34201781bd05a866665614457 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Sun, 20 Jul 2025 13:00:10 -0400 Subject: [PATCH 37/41] Add validations and fix bug of how items appear in cart --- app/server.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/app/server.js b/app/server.js index 5440cbf8..0bf6f0d4 100644 --- a/app/server.js +++ b/app/server.js @@ -24,6 +24,7 @@ const users = require('../initial-data/users.json'); const brands = require('../initial-data/brands.json'); const products = require('../initial-data/products.json'); + const verifyToken = (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader) { @@ -109,12 +110,28 @@ app.get('/me/cart', verifyToken, (req, res) => { app.post('/me/cart', verifyToken, (req, res) => { const { productId, quantity } = req.body; if (!productId || !quantity) { - return res.status(400).json({ error: 'Missing productId or quantity' }); + return res.status(400).json({ error: 'Missing productId and/or quantity' }); + } ; + if (typeof quantity !== 'number' || quantity <= 0){ + return res.status(422).json({ error: 'Quantity must be a positive number' }); } - const currentUser = users.find(user => user.login.username === req.username); - currentUser.cart.push({ productId, quantity }); - res.status(200).json({ - cart: currentUser.cart, + + const product = products.find(product => product.id === productId); + if (!product) { + return res.status(404).json({ error: 'Invalid Product ID' }) + }; + + const user = users.find(user => user.login.username === req.username); + + const existingItemIndex = user.cart.findIndex(item => item.productId === productId); + if (existingItemIndex >= 0) { + user.cart[existingItemIndex].quantity += quantity; + } else { + user.cart.push({ productId, quantity }); + }; + + return res.status(200).json({ + cart: user.cart, token: res.locals.newToken }); }); From 94876a1a7e9ed8ada11b014c31c469ec08e75d34 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 21 Jul 2025 12:39:47 -0400 Subject: [PATCH 38/41] add validation for productId to DELETE /me/cart/:productId --- app/server.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/app/server.js b/app/server.js index 0bf6f0d4..3b8748cf 100644 --- a/app/server.js +++ b/app/server.js @@ -139,11 +139,20 @@ app.post('/me/cart', verifyToken, (req, res) => { app.delete('/me/cart/:productId', verifyToken, (req, res) => { const user = users.find(user => user.login.username === req.username); const productId = req.params.productId; - user.cart = user.cart.filter(item => item.productId != productId); - res.status(200).json({ - cart: user.cart, - token: res.locals.newToken - }); + const product = products.find(product => product.id === productId); + + if (!product) { + return res.status(404).json({ error: 'Invalid Product ID' }); + }; + + const existingItemIndex = user.cart.findIndex(item => item.productId === productId); + if (existingItemIndex < 0) { + return res.status(404).json({ error: 'Product not in cart' }); + } else { + user.cart = user.cart.filter(item => item.productId != productId); + res.status(200).json({ cart: user.cart, token: res.locals.newToken }); + }; + }); app.put('/me/cart/:productId', verifyToken, (req, res) => { From dc992307550c154f9ab68b6156bf4f83beb642b6 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Mon, 21 Jul 2025 19:43:35 -0400 Subject: [PATCH 39/41] add validation for productId, quantity, and that the item exists in cart for PUT /me/cart/:productId --- app/server.js | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/app/server.js b/app/server.js index 3b8748cf..29219784 100644 --- a/app/server.js +++ b/app/server.js @@ -140,7 +140,6 @@ app.delete('/me/cart/:productId', verifyToken, (req, res) => { const user = users.find(user => user.login.username === req.username); const productId = req.params.productId; const product = products.find(product => product.id === productId); - if (!product) { return res.status(404).json({ error: 'Invalid Product ID' }); }; @@ -158,20 +157,31 @@ app.delete('/me/cart/:productId', verifyToken, (req, res) => { app.put('/me/cart/:productId', verifyToken, (req, res) => { const user = users.find(user => user.login.username === req.username); const productId = req.params.productId; - const { quantity } = req.body; - if (!quantity || typeof quantity !== 'number') { - return res.status(400).json({ error: 'Must use valid quantity' }); + const product = products.find(product => product.id === productId); + if (!product) { + return res.status(404).json({ error: 'Invalid Product ID' }); + }; + + const existingItemIndex = user.cart.findIndex(item => item.productId === productId); + if (existingItemIndex < 0) { + return res.status(404).json({ error: 'Item not found in cart' }); } - const item = user.cart.find(item => item.productId ===productId); - if (item) { - item.quantity = quantity; - res.status(200).json({ - cart: user.cart, - token: res.locals.newToken - }); - } else { - res.status(404).json({ error: 'Item not found in cart' }); + + const { quantity } = req.body; + if ((!quantity && quantity != 0) || typeof quantity !== 'number' || quantity < 0) { + return res.status(422).json({ error: 'Must use valid quantity' }); } + + if (quantity === 0) { + user.cart = user.cart.filter(item => item.productId != productId); + return res.status(200).json({ cart: user.cart, token: res.locals.newToken }); + }; + + user.cart[existingItemIndex].quantity = quantity; + return res.status(200).json({ + cart: user.cart, + token: res.locals.newToken + }); }); // Swagger From 55dddd1cc6a3db762b3deb67dd0426bbf7fc7265 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Tue, 22 Jul 2025 08:52:33 -0400 Subject: [PATCH 40/41] more Swagger updates --- swagger.yaml | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/swagger.yaml b/swagger.yaml index 1bb49639..16c132a6 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -57,9 +57,11 @@ paths: items: $ref: "#/definitions/Product" "404": - description: No products found for specified brand + description: Invalid ID schema: $ref: "#/definitions/ErrorResponse" + "422": + description: Out of Stock /login: post: @@ -77,6 +79,8 @@ paths: responses: "200": description: "Login successful" + schema: + $ref: "#/definitions/CartResponse" "401": description: "Invalid login info" schema: @@ -130,7 +134,15 @@ paths: schema: $ref: "#/definitions/CartResponse" "400": - description: "Missing id or quantity" + description: "Missing Product ID and/or quantity" + schema: + $ref: "#/definitions/ErrorResponse" + "404": + description: "Invalid Product ID" + schema: + $ref: "#/definitions/ErrorResponse" + "422": + description: "Quantity must be a positive number" schema: $ref: "#/definitions/ErrorResponse" @@ -157,6 +169,10 @@ paths: description: "Item deleted from cart" schema: $ref: "#/definitions/CartResponse" + "404": + description: "Product not found" + schema: + $ref: "#/definitions/ErrorResponse" put: tags: @@ -175,17 +191,24 @@ paths: type: string required: true description: "ID of product to change quantity of" + - in: body + name: itemQuantity + required: true + description: "Quantity of item to change" + schema: + $ref: "#/definitions/ItemQuantity" + responses: "200": description: "Quantity changed in cart" schema: $ref: "#/definitions/CartResponse" - "400": - description: "Invalid quantity" + "404": + description: "Invalid ID/Item not found" schema: $ref: "#/definitions/ErrorResponse" - "404": - description: "Item not found" + "422": + description: "Must use valid quantity" schema: $ref: "#/definitions/ErrorResponse" @@ -303,6 +326,16 @@ definitions: minimum: 1 example: 5 + ItemQuantity: + type: object + required: + - quantity + properties: + quantity: + type: integer + minimum: 0 + example: 5 + CartResponse: type: object properties: From 8126f0192055ef3cd3e5ca5241b53a3fca671ba4 Mon Sep 17 00:00:00 2001 From: Mark Smyth Date: Thu, 31 Jul 2025 17:30:28 -0400 Subject: [PATCH 41/41] Update README file, and dependencies --- README.md | 178 +++++++++++++++++++++++++++++++++++++++++++++- package-lock.json | 165 +++++++++++++++++++++++++++++++----------- 2 files changed, 299 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 96516fae..c385e0d6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,177 @@ -## Sunglasses.io Server +# Sunglasses.io Server -This project has been created by a student at Project Shift, a software engineering fellowship located in Downtown Durham. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. +> This project has been created by a student at Parsity, an online software engineering fellowship. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. +> +> If you have any questions about this project or the program in general, visit [Parsity.io](https://www.parsity.io/) -If you have any questions about this project or the program in general, visit projectshift.io or email hello@projectshift.io. +--- + +
+Welcome to the Sunglasses.io Server! This is a Node.js/Express-based backend project that provides a RESTful API for managing sunglasses brands, products, and user carts. The API uses JWT (JSON Web Token) authentication for protected routes, with a token renewal mechanism to extend session validity. + +## Project Overview + +This API allows users to: + +- Retrieve a list of brands and their products. +- Authenticate and manage a personal shopping cart with CRUD operations (create, read, update, delete). +- Test the API interactively via Swagger UI. + +## Requirements + +- **Node.js**: Version 14.x or higher (install from [nodejs.org](https://nodejs.org/)). +- **npm**: Comes with Node.js (verify with 'npm -v'). +- **Git**: For cloning the repository (install from [git-scm.com](http://git-scm.com/)). +- A code editor (e.g., Visual Studio Code) is recommended. + +## Getting Started + +
+ +**Install dependencies**: + +``` +npm install +``` + +- This installs required packages: `express`, `body-parser`, `jsonwebtoken`, `swagger-ui-express`, `yamljs`, and `cors`: + +- Ensure the node_modules folder is created and no errors occur during installation. +
+
+ +**Start the Server** + +``` +npm start +``` + +- The API runs on http://localhost:3000. + +- Press `Ctrl+C` to stop the server. +
+
+ +## Testing the API using Swagger UI + +
+ +- with server running, open browser and go to http://localhost:3000/api-docs to view interactive API documentation. + +
+ +**Non-Protected Routes** (can be accessed without authentication): + +- `GET /products` +- `GET /brands` +- `GET /brands/:id/products` + +
+ +**User Login Route** (access to cart and JWT): + +- `POST /login` + + - click "Try it out", + - sample account login is provided: + + ``` + {"username": "yellowleopard753", "password": "jonjon"} + ``` + + - click "Execute". + + - copy the token from the response (e.g., eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...) to paste into the next request. + +
+ +**Protected Routes** (require valid JWT token to access): + +- `GET /me/cart` +- `POST /me/cart` +- `DELETE /me/cart/:productId` +- `PUT /me/cart/:productId` + +
+ + **To access protected routes:** + + - all protected routes return a new token with an extended expiration (1 hour from request time) + - click "Try it out" + - in the "Authorization" box, paste the `token` with `Bearer` prefix + - token comes from either the initial login or a prior response + - ensure there is a space between `Bearer`(capital B) and the `token` and do not use quotes. + - example: + ``` + Bearer eyJhbGciOiJIUzI...9MiVqLdMrKohxSL4 + ``` + - if necessary, include any required parameters + - click "Execute" + - response will include an updated `cart` and a new `token` + - Repeat process for subsequent requests to maintain a valid session + +
+ +--- + +### Using Postman + +- server should be already running, if not use command: + ``` + npm start + ``` +- No authentication needed for **non-protected routes**: + + - `GET localhost:3000/products` + - `GET localhost:3000/brands` + - `GET localhost:3000/brands/:id/products` + +
+ +**Obtain a Token**: + +- Send a `POST` request to `localhost:3000/login` + + - include the following in the body: + ``` + {"username": "yellowleopard753", "password": "jonjon"} + ``` + +- Copy the `token` from the response to authenticate future requests. + +
+ +**Testing Protected Routes** (require valid JWT token to access): + +- Valid token must be provided to access these routes: + + - `GET localhost:3000/me/cart` + - `POST localhost:3000/me/cart` + - `DELETE localhost:3000/me/cart/:productId` + - `PUT localhost:3000/me/cart/:productId` + +
+ +- **Example** - create new `POST /me/cart` request which adds item to user's cart: + + - select POST + - enter `localhost:3000/me/cart` into the text box + - in "Authorization" tab, under "Auth Type" drowdown, select "Bearer Token" + - paste the `token` into the "Token" textbox + - if necessary, add any required parameters + - for `POST /me/cart` request, paste the following into req.body: + ``` + {"productId": "1", "quantity": 10} + ``` + - the response will include the updated `cart` and a new `token`. + +
+ +## Token Management Notes + +- The API uses a sliding expiration model: the token’s expiration is extended by 1 hour with each protected route request, and a new token is returned in the response. +- The initial token remains valid until its original 1-hour expiration, which is why you may not need to update it immediately in Postman or Swagger UI during short testing sessions. +
+
+ +**Important**: When using a frontend, the client must manage and use the new token from each response to ensure continuous validity. Failure to update the token after its expiration will result in a 401 Unauthorized error. diff --git a/package-lock.json b/package-lock.json index b045145c..1a06e06f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,29 @@ "mocha": "^10.2.0" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@types/chai": { "version": "4.3.11", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", @@ -183,9 +206,9 @@ } }, "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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -635,6 +658,22 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -784,13 +823,16 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -798,13 +840,14 @@ } }, "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", "dev": true, + "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0", "qs": "^6.11.0" }, @@ -944,9 +987,10 @@ } }, "node_modules/glob/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -996,6 +1040,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1017,15 +1077,6 @@ "he": "bin/he" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/http": { "version": "0.0.1-security", "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", @@ -2259,6 +2310,21 @@ } }, "dependencies": { + "@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true + }, + "@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "requires": { + "@noble/hashes": "^1.1.5" + } + }, "@types/chai": { "version": "4.3.11", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", @@ -2385,9 +2451,9 @@ } }, "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "requires": { "balanced-match": "^1.0.0" @@ -2716,6 +2782,18 @@ "es-errors": "^1.3.0" } }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2829,24 +2907,26 @@ "dev": true }, "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", "dev": true, "requires": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0", "qs": "^6.11.0" } @@ -2930,9 +3010,9 @@ }, "dependencies": { "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2973,6 +3053,15 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, "hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2987,12 +3076,6 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true - }, "http": { "version": "0.0.1-security", "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz",