From af15fc0dbea34731b34adc4da1723b29e5a6e53c Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Fri, 9 May 2025 22:18:00 +0100 Subject: [PATCH] Added more functionality --- package-lock.json | 2516 +++++++++++++++++++++++++- package.json | 9 +- src/index.js | 141 +- src/mongo/ReseedAction.js | 2 +- src/mongo/seedData.js | 6 +- src/passport.js | 29 +- src/routes/api/index.js | 16 +- src/routes/auth/index.js | 50 +- src/routes/fillaments/index.js | 47 - src/routes/gcodefiles/index.js | 59 +- src/routes/index.js | 36 +- src/routes/printers/index.js | 22 +- src/routes/printjobs/index.js | 25 +- src/schemas/fillament.schema.js | 23 - src/schemas/gcodefile.schema.js | 16 +- src/schemas/passwordResets.schema.js | 2 +- src/schemas/printer.schema.js | 51 +- src/schemas/printjob.schema.js | 27 +- src/schemas/user.schema.js | 26 +- src/services/auth/index.js | 625 +++---- src/services/fillaments/index.js | 121 -- src/services/gcodefiles/index.js | 337 +++- src/services/printers/index.js | 96 +- src/services/printjobs/index.js | 126 +- src/util/index.js | 249 ++- 25 files changed, 3749 insertions(+), 908 deletions(-) delete mode 100644 src/routes/fillaments/index.js delete mode 100644 src/schemas/fillament.schema.js delete mode 100644 src/services/fillaments/index.js diff --git a/package-lock.json b/package-lock.json index e24d993..3096f0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,17 +12,22 @@ "@simplewebauthn/server": "^10.0.0", "@tremor/react": "^3.17.2", "antd": "*", + "axios": "^1.8.4", "bcrypt": "*", "body-parser": "*", "cors": "^2.8.5", "dotenv": "*", - "express": "*", + "express": "^4.19.2", "express-session": "^1.18.0", + "i": "^0.3.7", "jsonwebtoken": "*", + "keycloak-connect": "^26.1.1", "log4js": "^6.9.1", "mongodb": "*", "mongoose": "*", "mongoose-sequence": "^6.0.1", + "mongoose-unique-array": "^0.4.2", + "multer": "^1.4.5-lts.1", "mysql": "^2.18.1", "mysql2": "^2.3.3", "node-cron": "^3.0.2", @@ -144,6 +149,740 @@ "react": ">=16.9.0" } }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.621.0.tgz", + "integrity": "sha512-FpXia5qFf6ijcNDWenVq+mP9r1LbiW/+52i9wrv2+Afi6Nn1ROf8W7St8WvE9TEZ3t78y+vis4CwqfGts+uiKA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/client-sts": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.621.0.tgz", + "integrity": "sha512-xpKfikN4u0BaUYZA9FGUMkkDmfoIP0Q03+A86WjqDWhcOoqNA1DkHsE4kZ+r064ifkPUfcNuUvlkVTEoBZoFjA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.621.0.tgz", + "integrity": "sha512-mMjk3mFUwV2Y68POf1BQMTF+F6qxt5tPu6daEUCNGC9Cenk3h2YXQQoS4/eSyYzuBiYk3vx49VgleRvdvkg8rg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.621.0.tgz", + "integrity": "sha512-707uiuReSt+nAx6d0c21xLjLm2lxeKc7padxjv92CIrIocnQSlJPxSCM7r5zBhwiahJA6MNQwmTl2xznU67KgA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.621.0.tgz", + "integrity": "sha512-CtOwWmDdEiINkGXD93iGfXjN0WmCp9l45cDWHHGa8lRgEDyhuL7bwd/pH5aSzj0j8SiQBG2k0S7DHbd5RaqvbQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/core": "^2.3.1", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.621.0.tgz", + "integrity": "sha512-Q+3awvTVJSqIGRjCUQflRwKPKlZ0TfmL3EQHgFLhZZrToeBapEA62+FY+T70aTKAZZZZprlvYeFPtBloNd5ziA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.621.0.tgz", + "integrity": "sha512-/jc2tEsdkT1QQAI5Dvoci50DbSxtJrevemwFsm0B73pwCcOQZ5ZwwSdVqGsPutzYzUVx3bcXg3LRL7jLACqRIg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.621.0.tgz", + "integrity": "sha512-0EWVnSc+JQn5HLnF5Xv405M8n4zfdx9gyGdpnCmAmFqEDHA8LmBdxJdpUk1Ovp/I5oPANhjojxabIW5f1uU0RA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.621.0.tgz", + "integrity": "sha512-4JqpccUgz5Snanpt2+53hbOBbJQrSFq7E1sAAbgY6BKVQUsW5qyXqnjvSF32kDeKa5JpBl3bBWLZl04IadcPHw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-ini": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.621.0.tgz", + "integrity": "sha512-Kza0jcFeA/GEL6xJlzR2KFf1PfZKMFnxfGzJzl5yN7EjoGdMijl34KaRyVnfRjnCWcsUpBWKNIDk9WZVMY9yiw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/client-sso": "3.621.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.621.0.tgz", + "integrity": "sha512-FQbC7I8ae/72ZekLBa45jWJ+Q3d+YPhc3bW/rCks6RrldM6RgLTGr8pTOPCxHl828ky10RjkBiBmVU818rliyw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.621.0", + "@aws-sdk/client-sso": "3.621.0", + "@aws-sdk/client-sts": "3.621.0", + "@aws-sdk/credential-provider-cognito-identity": "3.621.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-ini": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", + "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz", + "integrity": "sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", + "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", + "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", + "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", + "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", + "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@babel/cli": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.24.8.tgz", @@ -2797,6 +3536,610 @@ "integrity": "sha512-SFXke7xkgPRowY2E+8djKbdEznTVnD5R6GO7GPTthpHrokLvNKw8C3lFZypTxLI7KkCfGPfhtqB3d7OVGGa9jQ==", "license": "MIT" }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", + "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.3.1.tgz", + "integrity": "sha512-BC7VMXx/1BCmRPCVzzn4HGWAtsrb7/0758EtwOGFJQrlSwJBEjCcDLNZLFoL/68JexYa2s+KmgL/UfmXdG6v1w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", + "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", + "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.13.tgz", + "integrity": "sha512-zvCLfaRYCaUmjbF2yxShGZdolSHft7NNCTA28HVN9hKcEbOH+g5irr1X9s+in8EpambclGnevZY4A3lYpvDCFw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", + "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", + "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.11.tgz", + "integrity": "sha512-l0BpyYkciNyMaS+PnFFz4aO5sBcXvGLoJd7mX9xrMBIm2nIQBVvYgp2ZpPDMzwjKCavsXu06iuCm0F6ZJZc6yQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.13.tgz", + "integrity": "sha512-ZIRSUsnnMRStOP6OKtW+gCSiVFkwnfQF2xtf32QKAbHR6ACjhbAybDvry+3L5qQYdh3H6+7yD/AiUE45n8mTTw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.13.tgz", + "integrity": "sha512-voUa8TFJGfD+U12tlNNLCDlXibt9vRdNzRX45Onk/WxZe7TS+hTOZouEZRa7oARGicdgeXvt1A0W45qLGYdy+g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/config-resolver": "^3.0.5", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", + "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", + "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@tanstack/react-virtual": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.8.3.tgz", @@ -2824,6 +4167,20 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@testim/chrome-version": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", + "integrity": "sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g==", + "license": "MIT", + "optional": true + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT", + "optional": true + }, "node_modules/@tremor/react": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/@tremor/react/-/react-3.17.4.tgz", @@ -2959,6 +4316,16 @@ "@types/webidl-conversions": "*" } }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -3143,6 +4510,12 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -3395,6 +4768,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", @@ -3409,12 +4794,31 @@ "node": ">=12.0.0" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -3441,6 +4845,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", @@ -3489,6 +4904,36 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -3531,6 +4976,12 @@ "dev": true, "license": "MIT" }, + "node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -3570,6 +5021,13 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT", + "optional": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3592,6 +5050,12 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, "node_modules/browserslist": { "version": "4.23.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", @@ -3634,6 +5098,40 @@ "node": ">=16.20.1" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -3644,7 +5142,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, "node_modules/builtins": { @@ -3670,6 +5167,17 @@ "node": ">=10" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -3698,6 +5206,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3787,6 +5308,29 @@ "node": ">=10" } }, + "node_modules/chromedriver": { + "version": "135.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-135.0.1.tgz", + "integrity": "sha512-MLAS4t9dkttp1R1O2o/1nvtNIxg1dBTx7OE3ZCSrrFz+EFowd0wRAO7H5j918hw0i8+30yODq99p8CumvqRS9Q==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@testim/chrome-version": "^1.1.4", + "axios": "^1.7.4", + "compare-versions": "^6.1.0", + "extract-zip": "^2.0.1", + "proxy-agent": "^6.4.0", + "proxy-from-env": "^1.1.0", + "tcp-port-used": "^1.0.2" + }, + "bin": { + "chromedriver": "bin/chromedriver" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -3878,6 +5422,18 @@ "color-support": "bin.js" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -3895,6 +5451,13 @@ "dev": true, "license": "MIT" }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "license": "MIT", + "optional": true + }, "node_modules/compute-scroll-into-view": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", @@ -3907,6 +5470,21 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -4198,6 +5776,16 @@ "node": ">=12" } }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -4304,7 +5892,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/define-data-property": { @@ -4342,6 +5930,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -4440,6 +6052,20 @@ "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", "license": "MIT" }, + "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/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4536,6 +6162,21 @@ "dev": true, "license": "ISC" }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4551,6 +6192,16 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4630,13 +6281,10 @@ "license": "MIT" }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "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", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -4677,10 +6325,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, + "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" @@ -4690,15 +6337,15 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, + "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==", "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4814,6 +6461,28 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", @@ -5494,6 +7163,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -5524,7 +7207,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -5534,7 +7217,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -5673,6 +7356,27 @@ "type": "^2.7.2" } }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5720,6 +7424,29 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -5729,6 +7456,16 @@ "reusify": "^1.0.4" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "optional": true, + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -5836,6 +7573,26 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -5874,6 +7631,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -6054,16 +7826,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "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" @@ -6072,6 +7849,19 @@ "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/get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", @@ -6085,6 +7875,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "optional": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -6103,6 +7909,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -6164,12 +7985,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6223,6 +8044,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6232,9 +8054,9 @@ } }, "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" @@ -6247,7 +8069,6 @@ "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" @@ -6265,6 +8086,16 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "license": "ISC" }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -6277,6 +8108,17 @@ "node": ">= 0.4" } }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -6306,6 +8148,30 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -6319,6 +8185,14 @@ "node": ">= 6" } }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -6331,6 +8205,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -6431,6 +8325,29 @@ "node": ">=12" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6822,6 +8739,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT", + "optional": true + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -6865,6 +8789,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is2": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz", + "integrity": "sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==", + "license": "MIT", + "optional": true, + "dependencies": { + "deep-is": "^0.1.3", + "ip-regex": "^4.1.0", + "is-url": "^1.2.4" + }, + "engines": { + "node": ">=v0.10.0" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -7060,6 +8999,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -7197,6 +9142,17 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/jwk-to-pem": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.7.tgz", + "integrity": "sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ==", + "license": "Apache-2.0", + "dependencies": { + "asn1.js": "^5.3.0", + "elliptic": "^6.6.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", @@ -7208,14 +9164,29 @@ } }, "node_modules/kareem": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", - "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } }, + "node_modules/keycloak-connect": { + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/keycloak-connect/-/keycloak-connect-26.1.1.tgz", + "integrity": "sha512-2wvNJXldB9Em+mp6liJ+AnftcJovFEvNhUgv3hblNDmVihBoBqn4zFlwLIN41lo0H8CicB2T86xZ5U2MiQ9FFA==", + "license": "Apache-2.0", + "dependencies": { + "jwk-to-pem": "^2.0.0" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "chromedriver": "latest" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7438,6 +9409,15 @@ "semver": "bin/semver" } }, + "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", @@ -7545,6 +9525,18 @@ "node": ">= 0.6" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -7561,7 +9553,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7697,21 +9688,21 @@ } }, "node_modules/mongoose": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.5.1.tgz", - "integrity": "sha512-OhVcwVl91A1G6+XpjDcpkGP7l7ikZkxa0DylX7NT/lcEqAjggzSdqDxb48A+xsDxqNAr0ntSJ1yiE3+KJTOd5Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.13.0.tgz", + "integrity": "sha512-mieZBTtRIqA2xCGgl9Hlcr6fXU+AKNSOdeKfMYrb/IgdL3M/bDO4kYftsItIy86XyAoT5xV28alfCbMocFG8oA==", "license": "MIT", "dependencies": { - "bson": "^6.7.0", - "kareem": "2.6.3", - "mongodb": "6.7.0", + "bson": "^4.7.2", + "kareem": "2.5.1", + "mongodb": "4.17.2", "mpath": "0.9.0", - "mquery": "5.0.0", + "mquery": "4.0.3", "ms": "2.1.3", - "sift": "17.1.3" + "sift": "16.0.1" }, "engines": { - "node": ">=16.20.1" + "node": ">=12.0.0" }, "funding": { "type": "opencollective", @@ -7731,50 +9722,66 @@ "mongoose": ">=5" } }, - "node_modules/mongoose/node_modules/mongodb": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz", - "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==", - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.7.0", - "mongodb-connection-string-url": "^3.0.0" - }, + "node_modules/mongoose-unique-array": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/mongoose-unique-array/-/mongoose-unique-array-0.4.2.tgz", + "integrity": "sha512-9Z/2rvYip/WnjLM6WzyOse04P2s5pj/oE43kMiMkdi9U1a8wyt9rXoB2mtlo/PUptLvZtOOmOCREL/9tYo6liQ==", + "license": "Apache 2.0", "engines": { - "node": ">=16.20.1" + "node": ">= 12.0.0" }, "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", + "mongoose": "6.x" + } + }, + "node_modules/mongoose/node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/mongoose/node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "license": "Apache-2.0", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.2.tgz", + "integrity": "sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg==", + "license": "Apache-2.0", + "dependencies": { + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", "socks": "^2.7.1" }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0" + } + }, + "node_modules/mongoose/node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" } }, "node_modules/mongoose/node_modules/ms": { @@ -7783,6 +9790,31 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/mongoose/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongoose/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", @@ -7793,15 +9825,15 @@ } }, "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", + "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", "license": "MIT", "dependencies": { "debug": "4.x" }, "engines": { - "node": ">=14.0.0" + "node": ">=12.0.0" } }, "node_modules/ms": { @@ -7810,6 +9842,36 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mysql": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", @@ -7957,6 +10019,16 @@ "node": ">= 0.6" } }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -8380,6 +10452,64 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "optional": true, + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", @@ -8546,6 +10676,13 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT", + "optional": true + }, "node_modules/pg": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", @@ -8958,12 +11095,83 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -10452,9 +12660,9 @@ } }, "node_modules/sift": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", - "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==", "license": "MIT" }, "node_modules/signal-exit": { @@ -10494,11 +12702,60 @@ "node": ">=6" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -10543,6 +12800,12 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, "node_modules/sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -10675,6 +12938,14 @@ "node": ">= 4.0.0" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -10863,6 +13134,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT", + "optional": true + }, "node_modules/stylis": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", @@ -11073,6 +13351,35 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, + "node_modules/tcp-port-used": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", + "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4.3.1", + "is2": "^2.0.6" + } + }, + "node_modules/tcp-port-used/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -11355,6 +13662,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -11943,6 +14256,17 @@ "node": ">=10" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index e6cd96e..d2fc9e1 100644 --- a/package.json +++ b/package.json @@ -7,17 +7,22 @@ "@simplewebauthn/server": "^10.0.0", "@tremor/react": "^3.17.2", "antd": "*", + "axios": "^1.8.4", "bcrypt": "*", "body-parser": "*", "cors": "^2.8.5", "dotenv": "*", - "express": "*", + "express": "^4.19.2", "express-session": "^1.18.0", + "i": "^0.3.7", "jsonwebtoken": "*", + "keycloak-connect": "^26.1.1", "log4js": "^6.9.1", "mongodb": "*", "mongoose": "*", "mongoose-sequence": "^6.0.1", + "mongoose-unique-array": "^0.4.2", + "multer": "^1.4.5-lts.1", "mysql": "^2.18.1", "mysql2": "^2.3.3", "node-cron": "^3.0.2", @@ -43,7 +48,7 @@ "standard": "^17.1.0" }, "scripts": { - "start:dev": "nodemon --exec babel-node --experimental-specifier-resolution=node src/index.js", + "dev": "nodemon --exec babel-node --experimental-specifier-resolution=node src/index.js", "test": "echo \"Error: no test specified\" && exit 1", "seed": "node src/mongo/seedData.js", "clear": "node src/mongo/clearDbs.js" diff --git a/src/index.js b/src/index.js index 618b5f3..56fb430 100644 --- a/src/index.js +++ b/src/index.js @@ -1,60 +1,81 @@ -import express from "express"; -import bodyParser from "body-parser"; -import cors from "cors"; -import dotenv from "dotenv"; -import "./passport.js"; -import { dbConnect } from "./mongo/index.js"; -import { apiRoutes, authRoutes, printerRoutes, printJobRoutes, gcodeFileRoutes, fillamentRoutes } from "./routes/index.js"; -import path from "path"; -import * as fs from "fs"; -import cron from "node-cron"; -import ReseedAction from "./mongo/ReseedAction.js"; -import log4js from "log4js"; - -dotenv.config(); - -const PORT = process.env.PORT || 8080; -const app = express(); - -const logger = log4js.getLogger("App"); -logger.level = process.env.LOG_LEVEL; - -app.use(log4js.connectLogger(logger, { level: "trace" })); - -const whitelist = [process.env.APP_URL_CLIENT]; -const corsOptions = { - origin: function (origin, callback) { - if (!origin || whitelist.indexOf(origin) !== -1) { - callback(null, true); - } else { - callback(new Error("Not allowed by CORS")); - } - }, - credentials: true, -}; - -dbConnect(); - -app.use(cors(corsOptions)); -app.use(bodyParser.json({ type: "application/json", strict: false, limit: '50mb' })); -app.use(express.json()); - -app.get("/", function (req, res) { - const __dirname = fs.realpathSync("."); - res.sendFile(path.join(__dirname, "/src/landing/index.html")); -}); - -app.use("/auth", authRoutes); -app.use("/overview", apiRoutes); -app.use("/printers", printerRoutes); -app.use("/printjobs", printJobRoutes); -app.use("/gcodefiles", gcodeFileRoutes); -app.use("/fillaments", fillamentRoutes); - -if (process.env.SCHEDULE_HOUR) { - cron.schedule(`0 */${process.env.SCHEDULE_HOUR} * * *'`, () => { - ReseedAction(); - }); -} - -app.listen(PORT, () => logger.info(`Server listening to port ${PORT}`)); +import express from "express"; +import bodyParser from "body-parser"; +import cors from "cors"; +import dotenv from "dotenv"; +import { expressSession, keycloak } from "./keycloak.js"; +import { dbConnect } from "./mongo/index.js"; +import { + apiRoutes, + authRoutes, + printerRoutes, + printJobRoutes, + gcodeFileRoutes, + filamentRoutes, + spotlightRoutes, + partRoutes, + productRoutes, + vendorRoutes, + materialRoutes, +} from "./routes/index.js"; +import path from "path"; +import * as fs from "fs"; +import cron from "node-cron"; +import ReseedAction from "./mongo/ReseedAction.js"; +import log4js from "log4js"; + +dotenv.config(); + +const PORT = process.env.PORT || 8080; +const app = express(); + +const logger = log4js.getLogger("App"); +logger.level = process.env.LOG_LEVEL; + +app.use(log4js.connectLogger(logger, { level: "trace" })); + +const whitelist = [process.env.APP_URL_CLIENT]; +const corsOptions = { + origin: function (origin, callback) { + if (!origin || whitelist.indexOf(origin) !== -1) { + callback(null, true); + } else { + callback(new Error("Not allowed by CORS")); + } + }, + credentials: true, +}; + +dbConnect(); + +app.use(cors(corsOptions)); +app.use( + bodyParser.json({ type: "application/json", strict: false, limit: "50mb" }), +); +app.use(express.json()); +app.use(expressSession); +app.use(keycloak.middleware()); + +app.get("/", function (req, res) { + const __dirname = fs.realpathSync("."); + res.sendFile(path.join(__dirname, "/src/landing/index.html")); +}); + +app.use("/auth", authRoutes); +app.use("/overview", apiRoutes); +app.use("/spotlight", spotlightRoutes); +app.use("/printers", printerRoutes); +app.use("/printjobs", printJobRoutes); +app.use("/gcodefiles", gcodeFileRoutes); +app.use("/filaments", filamentRoutes); +app.use("/parts", partRoutes); +app.use("/products", productRoutes); +app.use("/vendors", vendorRoutes); +app.use("/materials", materialRoutes); + +if (process.env.SCHEDULE_HOUR) { + cron.schedule(`0 */${process.env.SCHEDULE_HOUR} * * *'`, () => { + ReseedAction(); + }); +} + +app.listen(PORT, () => logger.info(`Server listening to port ${PORT}`)); diff --git a/src/mongo/ReseedAction.js b/src/mongo/ReseedAction.js index 9778ca0..1409f1e 100644 --- a/src/mongo/ReseedAction.js +++ b/src/mongo/ReseedAction.js @@ -20,7 +20,7 @@ const ReseedAction = () => { name: "Admin", email: "admin@jsonapi.com", password: hashPassword, - created_at: new Date(), + createdAt: new Date(), profile_image: "../../images/admin.jpg", }; diff --git a/src/mongo/seedData.js b/src/mongo/seedData.js index 29378d0..3aedb62 100644 --- a/src/mongo/seedData.js +++ b/src/mongo/seedData.js @@ -14,7 +14,7 @@ async function seedDB() { name: "Admin", email: "admin@jsonapi.com", password: hashPassword, - created_at: new Date(), + createdAt: new Date(), profile_image: "../../images/admin.jpg", }; @@ -26,8 +26,8 @@ async function seedDB() { status : { type: "Queued" }, - created_at: new Date(), - updated_at: new Date(), + createdAt: new Date(), + updatedAt: new Date(), started_at: new Date(), }; diff --git a/src/passport.js b/src/passport.js index 3b4a92f..f8066e7 100644 --- a/src/passport.js +++ b/src/passport.js @@ -4,6 +4,8 @@ import dotenv from "dotenv"; import passport from "passport"; import { userModel } from "./schemas/user.schema.js"; +import { hostModel } from "./schemas/host.schema.js"; + const JWTStrategy = passportJWT.Strategy; dotenv.config(); @@ -14,14 +16,25 @@ passport.use( secretOrKey: process.env.JWT_SECRET, }, function (jwtPayload, done) { - return userModel - .findOne({ _id: jwtPayload.id }) - .then((user) => { - return done(null, user); - }) - .catch((err) => { - return done(err); - }); + if (jwtPayload.hostId) { + return hostModel + .findOne({ hostId: jwtPayload.hostId }) + .then((host) => { + return done(null, host); + }) + .catch((err) => { + return done(err); + }); + } else { + return userModel + .findOne({ _id: jwtPayload.id }) + .then((user) => { + return done(null, user); + }) + .catch((err) => { + return done(err); + }); + } } ) ); diff --git a/src/routes/api/index.js b/src/routes/api/index.js index e64534c..cb5b501 100644 --- a/src/routes/api/index.js +++ b/src/routes/api/index.js @@ -1,22 +1,26 @@ import express from "express"; -import passport from "passport"; -import jwt from 'jsonwebtoken'; + +import { keycloak, isAuthenticated } from "../../keycloak.js"; const router = express.Router(); -import { getProfileRouteHandler, patchProfileRouteHandler, getDashboardRouteHandler } from "../../services/api/index.js"; +import { + getProfileRouteHandler, + patchProfileRouteHandler, + getDashboardRouteHandler, +} from "../../services/api/index.js"; // get main dashboard info profile -router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => { +router.get("/", keycloak.protect(), (req, res) => { getDashboardRouteHandler(req, res); }); // get user's profile -router.get("/user", passport.authenticate('jwt',{session: false}), (req, res) => { +router.get("/user", isAuthenticated, (req, res) => { getProfileRouteHandler(req, res); }); // update user's profile -router.patch("/", passport.authenticate('jwt',{session: false}), async (req, res) => { +router.patch("/", isAuthenticated, async (req, res) => { patchProfileRouteHandler(req, res); }); diff --git a/src/routes/auth/index.js b/src/routes/auth/index.js index 81c9514..f133282 100644 --- a/src/routes/auth/index.js +++ b/src/routes/auth/index.js @@ -1,50 +1,34 @@ import express from "express"; -import passport from "passport"; - +import { isAuthenticated, keycloak } from "../../keycloak.js"; import { - getAuthModesHandler, forgotPasswordRouteHandler, loginRouteHandler, - registerPasskeyRouteHandler, - loginPasskeyRouteHandler, - registerRouteHandler, - resetPasswordRouteHandler, - validateTokenRouteHandler, + loginCallbackRouteHandler, + userRouteHandler, + logoutRouteHandler, + refreshTokenRouteHandler, } from "../../services/auth/index.js"; const router = express.Router(); -router.post("/modes", async (req, res, next) => { - const { email } = req.body; - await getAuthModesHandler(req, res, email); +router.get("/login", async (req, res) => { + loginRouteHandler(req, res); }); -router.post("/login", async (req, res, next) => { - const { email, password } = req.body; - await loginRouteHandler(req, res, email, password); +router.get("/callback", async (req, res) => { + loginCallbackRouteHandler(req, res); }); -router.post("/validate-token", async (req, res, next) => { - const { token } = req.body; - await validateTokenRouteHandler(req, res, token); +router.get("/refresh", async (req, res) => { + refreshTokenRouteHandler(req, res); }); -router.post("/logout", (req, res) => { - return res.sendStatus(204); +router.get("/user", isAuthenticated, async (req, res) => { + userRouteHandler(req, res); }); -router.post("/register", async (req, res) => { - const { name, email, password } = req.body; - await registerRouteHandler(req, res, name, email, password); -}); - -router.post("/passkey/register", passport.authenticate('jwt',{session: false}), async (req, res) => { - await registerPasskeyRouteHandler(req, res); -}); - -router.post("/passkey/login", async (req, res) => { - const { email, attestationResponse } = req.body; - await loginPasskeyRouteHandler(req, res, email, attestationResponse); +router.get("/logout", (req, res) => { + logoutRouteHandler(req, res); }); router.post("/password-forgot", async (req, res) => { @@ -52,8 +36,4 @@ router.post("/password-forgot", async (req, res) => { await forgotPasswordRouteHandler(req, res, email); }); -router.post("/password-reset", async (req, res) => { - await resetPasswordRouteHandler(req, res); -}); - export default router; diff --git a/src/routes/fillaments/index.js b/src/routes/fillaments/index.js deleted file mode 100644 index 5c257d3..0000000 --- a/src/routes/fillaments/index.js +++ /dev/null @@ -1,47 +0,0 @@ -import express from "express"; -import passport from "passport"; -import jwt from 'jsonwebtoken'; -import { parseStringIfNumber } from '../../util/index.js' - -const router = express.Router(); -import { listFillamentsRouteHandler, getFillamentRouteHandler, editFillamentRouteHandler, newFillamentRouteHandler } from "../../services/fillaments/index.js"; - -// list of fillaments -router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => { - const { page, limit, property } = req.query; - - const allowedFilters = [ - 'type', - 'brand', - 'diameter', - 'color' - ] - - const filter = {}; - - for (const [key, value] of Object.entries(req.query)) { - for (var i = 0; i < allowedFilters.length; i++) { - if (key == allowedFilters[i]) { - filter[key] = parseStringIfNumber(value); - } - } - - } - - listFillamentsRouteHandler(req, res, page, limit, property, filter); -}); - -router.post("/", passport.authenticate('jwt',{session: false}), (req, res) => { - newFillamentRouteHandler(req, res); -}); - -router.get("/:id", passport.authenticate('jwt',{session: false}), (req, res) => { - getFillamentRouteHandler(req, res); -}); - -// update printer info -router.put("/:id", passport.authenticate('jwt',{session: false}), async (req, res) => { - editFillamentRouteHandler(req, res); -}); - -export default router; diff --git a/src/routes/gcodefiles/index.js b/src/routes/gcodefiles/index.js index 6176701..528d484 100644 --- a/src/routes/gcodefiles/index.js +++ b/src/routes/gcodefiles/index.js @@ -1,23 +1,66 @@ import express from "express"; -import passport from "passport"; -import jwt from 'jsonwebtoken'; +import { isAuthenticated } from "../../keycloak.js"; +import { parseStringIfNumber } from "../../util/index.js"; const router = express.Router(); -import { listGCodeFilesRouteHandler, getGCodeFileRouteHandler, editGCodeFileRouteHandler } from "../../services/gcodefiles/index.js"; +import { + listGCodeFilesRouteHandler, + getGCodeFileRouteHandler, + editGCodeFileRouteHandler, + newGCodeFileRouteHandler, + parseGCodeFileHandler, + uploadGCodeFileContentRouteHandler, + getGCodeFileContentRouteHandler, +} from "../../services/gcodefiles/index.js"; // list of printers -router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => { - const { page, limit } = req.body; - listGCodeFilesRouteHandler(req, res, page, limit); +router.get("/", isAuthenticated, (req, res) => { + const { page, limit, property, search } = req.query; + + const allowedFilters = [ + "filament.type", + "filament.brand", + "filament.diameter", + "filament.color", + ]; + + const filter = {}; + + for (const [key, value] of Object.entries(req.query)) { + for (var i = 0; i < allowedFilters.length; i++) { + if (key == allowedFilters[i]) { + filter[key] = parseStringIfNumber(value); + } + } + } + + listGCodeFilesRouteHandler(req, res, page, limit, property, filter, search); }); -router.get("/:id", passport.authenticate('jwt',{session: false}), (req, res) => { +// new pritner +router.post("/", isAuthenticated, (req, res) => { + newGCodeFileRouteHandler(req, res); +}); + +router.get("/:id", isAuthenticated, (req, res) => { getGCodeFileRouteHandler(req, res); }); // update printer info -router.put("/:id", passport.authenticate('jwt',{session: false}), async (req, res) => { +router.put("/:id", isAuthenticated, async (req, res) => { editGCodeFileRouteHandler(req, res); }); +router.post("/:id/content", isAuthenticated, (req, res) => { + uploadGCodeFileContentRouteHandler(req, res); +}); + +router.post("/content", isAuthenticated, (req, res) => { + parseGCodeFileHandler(req, res); +}); + +router.get("/:id/content", isAuthenticated, (req, res) => { + getGCodeFileContentRouteHandler(req, res); +}); + export default router; diff --git a/src/routes/index.js b/src/routes/index.js index d23660f..55b2051 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,9 +1,27 @@ -import userRoutes from './users/index.js'; -import apiRoutes from './api/index.js'; -import authRoutes from './auth/index.js'; -import printerRoutes from './printers/index.js'; -import printJobRoutes from './printjobs/index.js'; -import gcodeFileRoutes from './gcodefiles/index.js' -import fillamentRoutes from './fillaments/index.js' - -export { userRoutes, apiRoutes, authRoutes, printerRoutes, printJobRoutes, gcodeFileRoutes, fillamentRoutes }; +import userRoutes from "./users/index.js"; +import apiRoutes from "./api/index.js"; +import authRoutes from "./auth/index.js"; +import printerRoutes from "./printers/index.js"; +import printJobRoutes from "./printjobs/index.js"; +import gcodeFileRoutes from "./gcodefiles/index.js"; +import filamentRoutes from "./filaments/index.js"; +import spotlightRoutes from "./spotlight/index.js"; +import partRoutes from "./parts/index.js"; +import productRoutes from "./products/index.js"; +import vendorRoutes from "./vendors/index.js"; +import materialRoutes from "./materials/index.js"; + +export { + userRoutes, + apiRoutes, + authRoutes, + printerRoutes, + printJobRoutes, + gcodeFileRoutes, + filamentRoutes, + spotlightRoutes, + partRoutes, + productRoutes, + vendorRoutes, + materialRoutes, +}; diff --git a/src/routes/printers/index.js b/src/routes/printers/index.js index 19d2f7b..43d9bca 100644 --- a/src/routes/printers/index.js +++ b/src/routes/printers/index.js @@ -1,25 +1,33 @@ import express from "express"; import passport from "passport"; -import jwt from 'jsonwebtoken'; +import { keycloak, isAuthenticated } from "../../keycloak.js"; const router = express.Router(); -import { listPrintersRouteHandler, editPrinterRouteHandler, getPrinterRouteHandler } from "../../services/printers/index.js"; +import { + listPrintersRouteHandler, + editPrinterRouteHandler, + getPrinterRouteHandler, + createPrinterRouteHandler, +} from "../../services/printers/index.js"; // list of printers -router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => { +router.get("/", isAuthenticated, (req, res) => { const { page, limit } = req.body; listPrintersRouteHandler(req, res, page, limit); }); -router.get("/:remoteAddress", passport.authenticate('jwt',{session: false}), (req, res) => { +// create new printer +router.post("/", isAuthenticated, (req, res) => { + createPrinterRouteHandler(req, res); +}); + +router.get("/:id", isAuthenticated, (req, res) => { getPrinterRouteHandler(req, res); }); // update printer info -router.put("/:remoteAddress", passport.authenticate('jwt',{session: false}), async (req, res) => { +router.put("/:id", isAuthenticated, async (req, res) => { editPrinterRouteHandler(req, res); }); - - export default router; diff --git a/src/routes/printjobs/index.js b/src/routes/printjobs/index.js index c9f4d0f..99320c2 100644 --- a/src/routes/printjobs/index.js +++ b/src/routes/printjobs/index.js @@ -1,22 +1,31 @@ import express from "express"; -import passport from "passport"; -import jwt from 'jsonwebtoken'; +import { isAuthenticated } from "../../keycloak.js"; const router = express.Router(); -import { listPrintJobsRouteHandler, getPrintJobRouteHandler, editPrintJobRouteHandler } from "../../services/printjobs/index.js"; +import { + listPrintJobsRouteHandler, + getPrintJobRouteHandler, + editPrintJobRouteHandler, + createPrintJobRouteHandler, +} from "../../services/printjobs/index.js"; -// list of printers -router.get("/", passport.authenticate('jwt',{session: false}), (req, res) => { +// list of print jobs +router.get("/", isAuthenticated, (req, res) => { const { page, limit } = req.body; listPrintJobsRouteHandler(req, res, page, limit); }); -router.get("/:jobNumber", passport.authenticate('jwt',{session: false}), (req, res) => { +// create new print job +router.post("/", isAuthenticated, (req, res) => { + createPrintJobRouteHandler(req, res); +}); + +router.get("/:id", isAuthenticated, (req, res) => { getPrintJobRouteHandler(req, res); }); -// update printer info -router.put("/:jobNumber", passport.authenticate('jwt',{session: false}), async (req, res) => { +// update job info +router.put("/:id", isAuthenticated, async (req, res) => { editPrintJobRouteHandler(req, res); }); diff --git a/src/schemas/fillament.schema.js b/src/schemas/fillament.schema.js deleted file mode 100644 index 9a5b453..0000000 --- a/src/schemas/fillament.schema.js +++ /dev/null @@ -1,23 +0,0 @@ -import mongoose from "mongoose"; - -const fillamentSchema = new mongoose.Schema({ - name: { required: true, type: String }, - barcode: { required: false, type: String }, - url: { required: false, type: String }, - image: { required: false, type: Buffer }, - color: { required: true, type: String }, - brand: { required: true, type: String }, - type: { required: true, type: String }, - price: { required: true, type: Number }, - diameter: { required: true, type: Number }, - created_at: { required: true, type: Date }, - updated_at: { required: true, type: Date }, -}); - -fillamentSchema.virtual("id").get(function () { - return this._id.toHexString(); -}); - -fillamentSchema.set("toJSON", { virtuals: true }); - -export const fillamentModel = mongoose.model("Fillament", fillamentSchema); diff --git a/src/schemas/gcodefile.schema.js b/src/schemas/gcodefile.schema.js index d1333ab..974a1b6 100644 --- a/src/schemas/gcodefile.schema.js +++ b/src/schemas/gcodefile.schema.js @@ -3,16 +3,18 @@ const { Schema } = mongoose; const gcodeFileSchema = new mongoose.Schema({ name: { required: true, type: String }, - gcodeFileName: { required: true, type: String }, + gcodeFileName: { required: false, type: String }, + gcodeFileInfo: { required: true, type: Object }, size: { type: Number, required: false }, - lines: { type: Number, required: false }, - fillament: { type: Schema.Types.ObjectId, ref: 'Fillament', required: true }, - image: { type: Buffer, required: false }, - printTimeMins: { type: Number, required: false }, - created_at: { type: Date }, - updated_at: { type: Date }, + filament: { type: Schema.Types.ObjectId, ref: "Filament", required: true }, + parts: [{ type: Schema.Types.ObjectId, ref: "Part", required: true }], + price: { type: Number, required: false }, + createdAt: { type: Date }, + updatedAt: { type: Date }, }); +gcodeFileSchema.index({ name: "text", brand: "text" }); + gcodeFileSchema.virtual("id").get(function () { return this._id.toHexString(); }); diff --git a/src/schemas/passwordResets.schema.js b/src/schemas/passwordResets.schema.js index c21c019..e5e7edf 100644 --- a/src/schemas/passwordResets.schema.js +++ b/src/schemas/passwordResets.schema.js @@ -3,7 +3,7 @@ import mongoose from "mongoose"; const passwordResetSchema = new mongoose.Schema({ email: { required: true, type: String }, token: { required: true, type: String }, - created_at: { type: Date }, + createdAt: { type: Date }, }); passwordResetSchema.virtual("id").get(function () { diff --git a/src/schemas/printer.schema.js b/src/schemas/printer.schema.js index f219f99..b321bd8 100644 --- a/src/schemas/printer.schema.js +++ b/src/schemas/printer.schema.js @@ -1,22 +1,49 @@ import mongoose from "mongoose"; +const { Schema } = mongoose; -const printerSchema = new mongoose.Schema({ - friendlyName: { required: true, type: String }, - online: { required: true, type: Boolean }, - status: { - type: { required: true, type: String }, - percent: { required: false, type: Number }, - }, - remoteAddress: { required: true, type: String }, - hostId: { required: true, type: String }, - connectedAt: { required: true, type: Date }, - loadedFillament: { required: true, type: Object } -}); +// Define the moonraker connection schema +const moonrakerSchema = new Schema( + { + host: { type: String, required: true }, + port: { type: Number, required: true }, + protocol: { type: String, required: true }, + apiKey: { type: String, default: null, required: false }, + }, + { _id: false }, +); +// Define the main printer schema +const printerSchema = new Schema( + { + printerName: { type: String, required: true }, + online: { type: Boolean, required: true, default: false }, + state: { + type: { type: String, required: true, default: "Offline" }, + percent: { type: Number, required: false }, + }, + connectedAt: { type: Date, default: null }, + loadedFilament: { + type: Schema.Types.ObjectId, + ref: "Filament", + default: null, + }, + moonraker: { type: moonrakerSchema, required: true }, + tags: [{ type: String }], + firmware: { type: String }, + currentJob: { type: Schema.Types.ObjectId, ref: "PrintJob" }, + currentSubJob: { type: Schema.Types.ObjectId, ref: "PrintSubJob" }, + subJobs: [{ type: Schema.Types.ObjectId, ref: "PrintSubJob" }], + }, + { timestamps: true }, +); + +// Add virtual id getter printerSchema.virtual("id").get(function () { return this._id.toHexString(); }); +// Configure JSON serialization to include virtuals printerSchema.set("toJSON", { virtuals: true }); +// Create and export the model export const printerModel = mongoose.model("Printer", printerSchema); diff --git a/src/schemas/printjob.schema.js b/src/schemas/printjob.schema.js index a6df55c..378a7d8 100644 --- a/src/schemas/printjob.schema.js +++ b/src/schemas/printjob.schema.js @@ -2,14 +2,27 @@ import mongoose from "mongoose"; const { Schema } = mongoose; const printJobSchema = new mongoose.Schema({ - status: { + state: { type: { required: true, type: String }, - printer: { type: Schema.Types.ObjectId, ref: 'Printer', required: false }, - }, - created_at: { required: true, type: Date }, - updated_at: { required: true, type: Date }, - started_at: { required: true, type: Date }, - gcode_file: { type: Schema.Types.ObjectId, ref: 'GCodeFile', required: false } + }, + printers: [{ type: Schema.Types.ObjectId, ref: "Printer", required: false }], + createdAt: { required: true, type: Date }, + updatedAt: { required: true, type: Date }, + startedAt: { required: true, type: Date }, + gcodeFile: { + type: Schema.Types.ObjectId, + ref: "GCodeFile", + required: false, + }, + quantity: { + type: Number, + required: true, + default: 1, + min: 1, + }, + subJobs: [ + { type: Schema.Types.ObjectId, ref: "PrintSubJob", required: false }, + ], }); printJobSchema.virtual("id").get(function () { diff --git a/src/schemas/user.schema.js b/src/schemas/user.schema.js index c03a189..c6537f5 100644 --- a/src/schemas/user.schema.js +++ b/src/schemas/user.schema.js @@ -4,19 +4,21 @@ import mongoose from "mongoose"; const userSchema = new mongoose.Schema({ name: { required: true, type: String }, email: { required: true, type: String }, - email_verified_at: { type: Date }, + emailVerifiedAt: { type: Date }, password: { required: true, type: String }, - webAuthnCredentials: [{ - id: String, - publicKey: Buffer, - counter: Number, - deviceType: String, - backedUp: Boolean, - transports: [String] - }], - profile_image: { type: String }, - created_at: { type: Date }, - updated_at: { type: Date }, + webAuthnCredentials: [ + { + id: String, + publicKey: Buffer, + counter: Number, + deviceType: String, + backedUp: Boolean, + transports: [String], + }, + ], + profileImage: { type: String }, + createdAt: { type: Date }, + updatedAt: { type: Date }, }); userSchema.virtual("id").get(function () { diff --git a/src/services/auth/index.js b/src/services/auth/index.js index 4b19fbf..c3e711b 100644 --- a/src/services/auth/index.js +++ b/src/services/auth/index.js @@ -1,376 +1,297 @@ import dotenv from "dotenv"; -import nodemailer from "nodemailer"; -import randomToken from "random-token"; -import bcrypt from "bcrypt"; -import url from "url"; -import { userModel } from "../../schemas/user.schema.js"; -import { passwordResetModel } from "../../schemas/passwordResets.schema.js"; -import { - generateRegistrationOptions, - verifyRegistrationResponse, - generateAuthenticationOptions, - verifyAuthenticationResponse, -} from "@simplewebauthn/server"; -import { isoUint8Array } from "@simplewebauthn/server/helpers"; - -import jwt from "jsonwebtoken"; +import { keycloak } from "../../keycloak.js"; import log4js from "log4js"; +import axios from "axios"; + +dotenv.config(); const logger = log4js.getLogger("Auth"); logger.level = process.env.LOG_LEVEL; -dotenv.config(); +// Login handler +export const loginRouteHandler = (req, res) => { + // Get the redirect URL from form data or default to production overview + const redirectUrl = req.query.redirect_uri || "/production/overview"; -let challenges = {}; + // Store the original URL to redirect after login + const authUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/auth`; + const callbackUrl = encodeURIComponent( + `${process.env.APP_URL_API}/auth/callback`, + ); + const state = encodeURIComponent(redirectUrl); -const rpName = "Farm Control"; -const rpID = url.parse(process.env.APP_URL_CLIENT).host; -const origin = `https://${rpID}`; + logger.warn(req.query.redirect_uri); -const transporter = nodemailer.createTransport({ - host: "smtp.mailtrap.io", - port: 2525, - auth: { - user: process.env.MAILTRAP_USER, - pass: process.env.MAILTRAP_PASSWORD, - }, -}); - -function generateToken() { - -} - - -export const getAuthModesHandler = async (req, res, email) => { - let foundUser = await userModel.findOne({ email: email }); - if (foundUser == null) { - return res.status(400).json({ - error: "Invalid email address.", - }); - } - if (foundUser.webAuthnCredentials.length > 0) { - return res.status(200).json({ - authModes: ["password", "passkey"], - }); - } else { - return res.status(200).json({ - authModes: ["password"], - }); - } + res.redirect( + `${authUrl}?client_id=${process.env.KEYCLOAK_CLIENT_ID}&redirect_uri=${callbackUrl}&response_type=code&scope=openid&state=${state}`, + ); }; -export const loginRouteHandler = async (req, res, email, password) => { - //Check If User Exists - let foundUser = await userModel.findOne({ email: email }); - if (foundUser == null) { - return res.status(400).json({ - error: "Invalid credentials.", +// Login callback handler +export const loginCallbackRouteHandler = (req, res) => { + // Don't use keycloak.protect() here as it expects an already authenticated session + + // Extract the code and state from the query parameters + const code = req.query.code; + const state = req.query.state || "/production/overview"; + + if (!code) { + return res.status(400).send("Authorization code missing"); + } + + // Exchange the code for tokens manually + const tokenUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`; + const redirectUri = `${process.env.APP_URL_API || "http://localhost:8080"}/auth/callback`; + + // Make a POST request to exchange the code for tokens + axios + .post( + tokenUrl, + new URLSearchParams({ + grant_type: "authorization_code", + client_id: process.env.KEYCLOAK_CLIENT_ID, + client_secret: process.env.KEYCLOAK_CLIENT_SECRET, + code: code, + redirect_uri: redirectUri, + }).toString(), + { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }, + ) + .then((response) => { + // Store tokens in session + req.session["keycloak-token"] = { + access_token: response.data.access_token, + refresh_token: response.data.refresh_token, + id_token: response.data.id_token, + expires_at: new Date().getTime() + response.data.expires_in * 1000, + }; + // Save session and redirect to the original URL + req.session.save(() => { + res.redirect( + (process.env.APP_URL_CLIENT || "http://localhost:3000") + state, + ); + }); + }) + .catch((error) => { + console.error( + "Token exchange error:", + error.response?.data || error.message, + ); + res.status(500).send("Authentication failed"); }); - } else { - const validPassword = await bcrypt.compare(password, foundUser.password); - if (validPassword) { - // Generate JWT token - const token = jwt.sign( - { id: foundUser.id, email: foundUser.email }, - process.env.JWT_SECRET, +}; + +export const userRouteHandler = (req, res) => { + if (req.session && req.session["keycloak-token"]) { + const token = req.session["keycloak-token"]; + const userInfoUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/userinfo`; + // User is authenticated + // Extract user info from the token + // + logger.info("Fetching user from keycloak..."); + axios + .post( + userInfoUrl, + new URLSearchParams({ + client_id: process.env.KEYCLOAK_CLIENT_ID, + client_secret: process.env.KEYCLOAK_CLIENT_SECRET, + }), { - expiresIn: "24h", - } - ); - return res.json({ - user: { - id: foundUser.id, - name: foundUser.name, - email: foundUser.email, + headers: { + Authorization: `Bearer ${token.access_token}`, + }, }, - access_token: token, + ) + .then((response) => { + const userInfo = { + // Extract user details from token + // This depends on your token structure + access_token: token.access_token, + expires_at: token.expires_at, + roles: token.realm_access?.roles || [], + username: response.data.preferred_username, + email: response.data.email, + name: response.data.name, + firstName: response.data.given_name, + lastName: response.data.family_name, + }; + res.json(userInfo); + }) + .catch((error) => { + logger.error( + "Token exchange error:", + error.response?.data || error.message, + ); + res.status(500).send("Authentication failed"); }); - } else { - return res.status(400).json({ - error: "Invalid credentials.", - }); - } - } -}; - -export const validateTokenRouteHandler = async (req, res, token) => { - try { - jwt.verify(token, process.env.JWT_SECRET); - res.status(200).send({ - status: "OK", - }); - } catch (err) { - console.error("Token verification error:", err); - res.status(401).send("Invalid token"); - } -}; - -export const registerPasskeyRouteHandler = async (req, res) => { - // check to see if the request has provided a user - const user = req.user; - if (!user) { - // if no user exists - return res.status(400).json({ error: "User not specified." }); - } - if (req.body.token) { - const options = await generateRegistrationOptions({ - rpName: rpName, - rpID: rpID, - userName: user.email, - userDisplayName: user.name, - excludeCredentials: user.webAuthnCredentials.map( - (webAuthnCredential) => ({ - id: webAuthnCredential.id, - transports: webAuthnCredential.transports, - }) - ), - attestationType: "none", - authenticatorSelection: { - residentKey: "preferred", - userVerification: "preferred", - authenticatorAttachment: "platform", - }, - }); - - challenges[user.id] = options.challenge; - return res.status(200).send(options); - } - - const expectedChallenge = challenges[user.id]; - const attestationResponse = req.body; - - let verification; - - try { - verification = await verifyRegistrationResponse({ - response: attestationResponse, - expectedChallenge, - expectedOrigin: process.env.APP_URL_CLIENT, - expectedRPID: url.parse(process.env.APP_URL_CLIENT).host, - }); - - const { registrationInfo } = verification; - const { - credentialID, - credentialPublicKey, - counter, - credentialDeviceType, - credentialBackedUp, - } = registrationInfo; - - const webAuthnCredential = { - id: credentialID, - publicKey: Buffer.from(new Uint8Array(credentialPublicKey)), - counter, - deviceType: credentialDeviceType, - backedUp: credentialBackedUp, - transports: attestationResponse.response.transports, - }; - - console.log(webAuthnCredential); - user.webAuthnCredentials.push(webAuthnCredential); - - await user.save(); - res.status(200).send({ status: "OK" }); - } catch (error) { - console.log(error); - return res.status(400).json({ error: error.message }); - } - - if (verification.verified) { } else { - res.status(400).send({ error: "Not verified." }); + // User is not authenticated + res.status(401).json({ error: "Not authenticated" }); } }; -export const loginPasskeyRouteHandler = async ( - req, - res, - email, - attestationResponse -) => { - if (!email) { - return; - } - let user = await userModel.findOne({ email: email }); - if (user == null) { - return res.status(400).json({ - error: "Invalid email address.", - }); - } - if (attestationResponse) { - logger.info("Verfifying challenge..."); - const expectedChallenge = challenges[user.id]; - let verification; - try { - const webAuthnCredentialIndex = user.webAuthnCredentials.findIndex( - (cred) => cred.id === attestationResponse.id - ); - const webAuthnCredential = user.webAuthnCredentials[webAuthnCredentialIndex]; - verification = await verifyAuthenticationResponse({ - response: attestationResponse, - expectedChallenge, - expectedOrigin: process.env.APP_URL_CLIENT, - expectedRPID: url.parse(process.env.APP_URL_CLIENT).host, - authenticator: { - credentialID: webAuthnCredential.id, - credentialPublicKey: new Uint8Array(webAuthnCredential.publicKey), - counter: webAuthnCredential.counter, - transports: webAuthnCredential.transports, - }, - }); - user.webAuthnCredentials[webAuthnCredentialIndex].counter = verification.authenticationInfo.newCounter; // Update connection counter - await user.save(); +// Logout handler +export const logoutRouteHandler = (req, res) => { + // Get the redirect URL from query or default to login page + const redirectUrl = req.query.redirect_uri || "/login"; - // Generate JWT token - const token = jwt.sign( - { id: user.id, email: user.email }, - process.env.JWT_SECRET, - { - expiresIn: "24h", - } - ); - - return res.json({ - user: { - id: user.id, - name: user.name, - email: user.email, - }, - access_token: token, - }); - - } catch (error) { - console.log(error); - res.status(400).send({ error }); - } - } else { - // Get options - logger.info("Sending authentication options..."); - const options = await generateAuthenticationOptions({ - rpID: url.parse(process.env.APP_URL_CLIENT).host, - allowCredentials: user.webAuthnCredentials.map((cred) => ({ - id: cred.id, - type: "public-key", - transports: cred.transports, - })), - }); - challenges[user.id] = options.challenge; - res.status(200).send(options); - } -}; - -export const registerRouteHandler = async (req, res, name, email, password) => { - // check if user already exists - let foundUser = await userModel.findOne({ email: email }); - if (foundUser) { - // does not get the error - return res.status(400).json({ message: "Email is already in use" }); - } - - // check password to exist and be at least 8 characters long - if (!password || password.length < 8) { - return res - .status(400) - .json({ message: "Password must be at least 8 characters long." }); - } - - // hash password to save in db - const salt = await bcrypt.genSalt(10); - const hashPassword = await bcrypt.hash(password, salt); - - const newUser = new userModel({ - name: name, - email: email, - password: hashPassword, - }); - await newUser.save(); - - // Generate JWT token - const token = jwt.sign({ id: newUser.id, email: newUser.email }, "token", { - expiresIn: "24h", - }); - return res.status(200).json({ - token_type: "Bearer", - expires_in: "24h", - access_token: token, - refresh_token: token, - }); -}; - -export const forgotPasswordRouteHandler = async (req, res, email) => { - let foundUser = await userModel.findOne({ email: email }); - - if (!foundUser) { - return res.status(400).json({ - errors: { email: ["The email does not match any existing user."] }, - }); - } else { - let token = randomToken(20); - // send mail with defined transport object - let info = await transporter.sendMail({ - from: "admin@jsonapi.com", // sender address - to: email, // list of receivers - subject: "Reset Password", // Subject line - html: `

You requested to change your password.If this request was not made by you please contact us. Access this link to reste your password

`, // html body - }); - const dataSent = { - data: "password-forgot", - attributes: { - redirect_url: `${process.env.APP_URL_API}/password-reset`, - email: email, - }, - }; - - // save token in db - await passwordResetModel.create({ - email: foundUser.email, - token: token, - created_at: new Date(), - }); - - return res.status(204).json(dataSent); - } -}; - -export const resetPasswordRouteHandler = async (req, res) => { - const foundUser = await userModel.findOne({ - email: req.body.data.attributes.email, - }); - - if (!foundUser || !foundToken) { - return res.status(400).json({ - errors: { - email: ["The email or token does not match any existing user."], - }, - }); - } else { - const { password, password_confirmation } = req.body.data.attributes; - // validate password - if (password.length < 8) { - return res.status(400).json({ - errors: { - password: ["The password should have at lest 8 characters."], - }, - }); + // Destroy the session + req.session.destroy((err) => { + if (err) { + logger.error("Error destroying session:", err); + return res.status(500).json({ error: "Failed to logout" }); } - if (password != password_confirmation) { - return res.status(400).json({ - errors: { - password: ["The password and password confirmation must match."], - }, - }); - } - const salt = await bcrypt.genSalt(10); - const hashPassword = await bcrypt.hash(password, salt); - - await passwordResetModel.deleteOne({ email: foundUser.email }); - - await userModel.updateOne( - { email: foundUser.email }, - { $set: { password: hashPassword } } + // Construct the Keycloak logout URL with the redirect URI + const logoutUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/logout`; + const encodedRedirectUri = encodeURIComponent( + `${process.env.APP_URL_CLIENT}${redirectUrl}`, ); - return res.sendStatus(204); - } + + // Redirect to Keycloak logout with the redirect URI + res.redirect( + `${logoutUrl}?client_id=${process.env.KEYCLOAK_CLIENT_ID}&post_logout_redirect_uri=${encodedRedirectUri}`, + ); + }); }; + +// Token validation - protected route middleware +export const validateTokenMiddleware = keycloak.protect(); + +// Check if user has a specific role +export const hasRole = (role) => { + return keycloak.protect((token) => { + return token && token.hasRole(role); + }); +}; + +// Get user info from the token +export const getUserInfoHandler = (req, res) => { + if (req.kauth && req.kauth.grant) { + const token = req.kauth.grant.access_token; + const userInfo = { + id: token.content.sub, + email: token.content.email, + name: + token.content.name || + `${token.content.given_name || ""} ${token.content.family_name || ""}`.trim(), + roles: token.content.realm_access?.roles || [], + }; + return res.json(userInfo); + } + return res.status(401).json({ error: "Not authenticated" }); +}; + +// Register route - Since we're using Keycloak, registration should be handled there +// This endpoint will redirect to Keycloak's registration page +export const registerRouteHandler = (req, res) => { + const registrationUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/registrations`; + const redirectUri = encodeURIComponent( + process.env.APP_URL_CLIENT + "/auth/login", + ); + + res.redirect( + `${registrationUrl}?client_id=${process.env.KEYCLOAK_CLIENT_ID}&redirect_uri=${redirectUri}`, + ); +}; + +// Forgot password handler - redirect to Keycloak's reset password page +export const forgotPasswordRouteHandler = (req, res) => { + const resetUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/login-actions/reset-credentials`; + const redirectUri = encodeURIComponent( + process.env.APP_URL_CLIENT + "/auth/login", + ); + + res.redirect( + `${resetUrl}?client_id=${process.env.KEYCLOAK_CLIENT_ID}&redirect_uri=${redirectUri}`, + ); +}; + +// Refresh token handler +export const refreshTokenRouteHandler = (req, res) => { + if ( + !req.session || + !req.session["keycloak-token"] || + !req.session["keycloak-token"].refresh_token + ) { + return res.status(401).json({ error: "No refresh token available" }); + } + + const refreshToken = req.session["keycloak-token"].refresh_token; + const tokenUrl = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`; + + axios + .post( + tokenUrl, + new URLSearchParams({ + grant_type: "refresh_token", + client_id: process.env.KEYCLOAK_CLIENT_ID, + client_secret: process.env.KEYCLOAK_CLIENT_SECRET, + refresh_token: refreshToken, + }).toString(), + { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }, + ) + .then((response) => { + // Update session with new tokens + req.session["keycloak-token"] = { + ...req.session["keycloak-token"], + access_token: response.data.access_token, + refresh_token: response.data.refresh_token, + expires_at: new Date().getTime() + response.data.expires_in * 1000, + }; + + // Save session and return new token info + req.session.save(() => { + res.json({ + access_token: response.data.access_token, + expires_at: req.session["keycloak-token"].expires_at, + }); + }); + }) + .catch((error) => { + logger.error( + "Token refresh error:", + error.response?.data || error.message, + ); + + // If refresh token is invalid, clear the session + if (error.response?.status === 400) { + req.session.destroy(); + } + + res.status(500).json({ error: "Failed to refresh token" }); + }); +}; + +// Example of how to set up your routes in Express +/* +import express from "express"; +const app = express(); + +// Apply session middleware +app.use(sessionMiddleware); + +// Initialize Keycloak middleware +app.use(keycloak.middleware()); + +// Set up routes +app.get('/auth/login', loginRouteHandler); +app.get('/auth/logout', logoutRouteHandler); +app.get('/auth/register', registerRouteHandler); +app.get('/auth/forgot-password', forgotPasswordRouteHandler); + +// Protected route example +app.get('/api/profile', validateTokenMiddleware, getUserInfoHandler); + +// Admin-only route example +app.get('/api/admin', hasRole('admin'), (req, res) => { + res.json({ message: 'Admin access granted' }); +}); +*/ diff --git a/src/services/fillaments/index.js b/src/services/fillaments/index.js deleted file mode 100644 index 48a03a6..0000000 --- a/src/services/fillaments/index.js +++ /dev/null @@ -1,121 +0,0 @@ -import dotenv from "dotenv"; -import { fillamentModel } from "../../schemas/fillament.schema.js" -import jwt from "jsonwebtoken"; -import log4js from "log4js"; -import mongoose from "mongoose"; - -dotenv.config(); - -const logger = log4js.getLogger("Fillaments"); -logger.level = process.env.LOG_LEVEL; - -export const listFillamentsRouteHandler = async (req, res, page = 1, limit = 25, property = "", filter = {}) => { - try { - // Calculate the skip value based on the page number and limit - const skip = (page - 1) * limit; - - - let fillament; - let aggregateCommand = []; - - if (filter != {}) { // use filtering if present - aggregateCommand.push({ $match: filter }); - } - - if (property != "") { - aggregateCommand.push({ $group: { _id: `$${property}` } }) // group all same properties - aggregateCommand.push({ $project: { _id: 0, [property]: "$_id" }}); // rename _id to the property name - } else { - aggregateCommand.push({ $project: { image: 0, url: 0 }}); - } - - aggregateCommand.push({ $skip: skip }); - aggregateCommand.push({ $limit: Number(limit) }); - - console.log(aggregateCommand) - - fillament = await fillamentModel.aggregate(aggregateCommand) - - logger.trace(`List of filaments (Page ${page}, Limit ${limit}, Property ${property}):`, fillament); - res.send(fillament); - } catch (error) { - logger.error("Error listing filaments:", error); - res.status(500).send({ error: error }); - } -}; - -export const getFillamentRouteHandler = async (req, res) => { - try { - // Get ID from params - const id = new mongoose.Types.ObjectId(req.params.id); - // Fetch the fillament with the given remote address - const fillament = await fillamentModel.findOne({ - _id: id - }); - - if (!fillament) { - logger.warn(`Fillament not found with supplied id.`); - return res.status(404).send({ error: "Print job not found." }); - } - - logger.trace(`Fillament with ID: ${id}:`, fillament); - res.send(fillament); - } catch (error) { - logger.error("Error fetching Fillament:", error); - res.status(500).send({ error: error.message }); - } -}; - -export const editFillamentRouteHandler = async (req, res) => { - try { - // Get ID from params - const id = new mongoose.Types.ObjectId(req.params.id); - // Fetch the fillament with the given remote address - const fillament = await fillamentModel.findOne({ _id: id }); - - if (!fillament) { // Error handling - logger.warn(`Fillament not found with supplied id.`); - return res.status(404).send({ error: "Print job not found." }); - } - - logger.trace(`Fillament with ID: ${id}:`, fillament); - - try { - const { created_at, updated_at, started_at, status, ...updateData } = req.body; - - const result = await fillamentModel.updateOne( - { _id: id }, - { $set: updateData } - ); - if (result.nModified === 0) { - logger.error("No Fillament updated."); - res.status(500).send({ error: "No fillaments updated." }); - } - } catch (updateError) { - logger.error("Error updating fillament:", updateError); - res.status(500).send({ error: updateError.message }); - } - res.send("OK"); - } catch (fetchError) { - logger.error("Error fetching fillament:", fetchError); - res.status(500).send({ error: fetchError.message }); - } -}; - -export const newFillamentRouteHandler = async (req, res) => { - - try { - let { ...newFillament } = req.body; - newFillament = { ...newFillament, created_at: new Date(), updated_at: new Date() } - - const result = await fillamentModel.create(newFillament); - if (result.nCreated === 0) { - logger.error("No fillament created."); - res.status(500).send({ error: "No fillament created." }); - } - res.status(200).send({ status: "ok" }); - } catch (updateError) { - logger.error("Error updating fillament:", updateError); - res.status(500).send({ error: updateError.message }); - } -}; \ No newline at end of file diff --git a/src/services/gcodefiles/index.js b/src/services/gcodefiles/index.js index 5645555..48b22ba 100644 --- a/src/services/gcodefiles/index.js +++ b/src/services/gcodefiles/index.js @@ -1,55 +1,182 @@ import dotenv from "dotenv"; -import { gcodeFileModel } from "../../schemas/gcodefile.schema.js" +import { gcodeFileModel } from "../../schemas/gcodefile.schema.js"; import jwt from "jsonwebtoken"; import log4js from "log4js"; +import multer from "multer"; +import crypto from "crypto"; +import path from "path"; +import fs from "fs"; +import mongoose from "mongoose"; + +import { extractConfigBlock } from "../../util/index.js"; dotenv.config(); const logger = log4js.getLogger("GCodeFiles"); logger.level = process.env.LOG_LEVEL; +// Set storage engine +const gcodeStorage = multer.diskStorage({ + destination: process.env.GCODE_STORAGE, + filename: async function (req, file, cb) { + // Retrieve custom file name from request body + const customFileName = req.params.id || "default"; // Default to 'default' if not provided + + // Create the final filename ensuring it ends with .gcode + const finalFilename = `${customFileName}.gcode`; + + // Call callback with the final filename + cb(null, finalFilename); + }, +}); + +// Initialise upload +const gcodeUpload = multer({ + storage: gcodeStorage, + limits: { fileSize: 500000000 }, // 50MB limit + fileFilter: function (req, file, cb) { + checkFileType(file, cb); + }, +}).single("gcodeFile"); // The name attribute of the file input in the HTML form + +// Check file type +function checkFileType(file, cb) { + // Allowed ext + const filetypes = /g|gco|gcode/; + // Check ext + const extname = filetypes.test(path.extname(file.originalname).toLowerCase()); + + if (extname) { + console.log(file); + return cb(null, true); + } else { + cb("Error: .g, .gco, and .gcode files only!"); + } +} + export const listGCodeFilesRouteHandler = async ( req, - res,) => { + res, + page = 1, + limit = 25, + property = "", + filter = {}, + search = "", +) => { try { - - // Fetch gcode files and group - const gcodeFiles = await gcodeFileModel.aggregate([ - { - $group: { - _id: "$status", - totalQuantity: { $sum: "$quantity" }, - totalPrice: { $sum: "$price" }, - orders: { $push: "$$ROOT" } - } - } - ]); + // Calculate the skip value based on the page number and limit + const skip = (page - 1) * limit; - logger.trace(`List of print jobs (Page ${page}, Limit ${limit}):`); + let gcodeFile; + let aggregateCommand = []; + + if (search) { + // Add a text search match stage for name and brand fields + aggregateCommand.push({ + $match: { + $text: { + $search: search, + }, + }, + }); + } + + aggregateCommand.push({ + $lookup: { + from: "filaments", // The name of the Filament collection + localField: "filament", + foreignField: "_id", + as: "filament", + }, + }); + + aggregateCommand.push({ + $unwind: { + path: "$filament", + preserveNullAndEmptyArrays: true, // Keep documents without a matching filament + }, + }); + + aggregateCommand.push({ + $addFields: { + filament: "$filament", + }, + }); + + if (filter != {}) { + // use filtering if present + aggregateCommand.push({ $match: filter }); + } + + if (property != "") { + aggregateCommand.push({ $group: { _id: `$${property}` } }); // group all same properties + aggregateCommand.push({ $project: { _id: 0, [property]: "$_id" } }); // rename _id to the property name + } else { + aggregateCommand.push({ + $project: { + "filament.gcodeFileInfo.estimatedPrintingTimeNormalMode": 0, + url: 0, + "filament.image": 0, + "filament.createdAt": 0, + "filament.updatedAt": 0, + }, + }); + } + + aggregateCommand.push({ $skip: skip }); + aggregateCommand.push({ $limit: Number(limit) }); + + console.log(aggregateCommand); + + gcodeFile = await gcodeFileModel.aggregate(aggregateCommand); + + logger.trace( + `List of gcode files (Page ${page}, Limit ${limit}, Property ${property}):`, + gcodeFile, + ); res.send(gcodeFile); } catch (error) { - logger.error("Error listing print jobs:", error); + logger.error("Error listing gcode files:", error); res.status(500).send({ error: error }); } }; -export const getGCodeFileRouteHandler = async (req, res) => { +export const getGCodeFileContentRouteHandler = async (req, res) => { try { // Get ID from params const id = new mongoose.Types.ObjectId(req.params.id); // Fetch the gcodeFile with the given remote address - const gcodeFile = await gcodeFileModel.findOne({ - _id: id + const gcodeFile = await gcodeFileModel.findOne({ + _id: id, }); - + if (!gcodeFile) { logger.warn(`GCodeFile not found with supplied id.`); return res.status(404).send({ error: "Print job not found." }); } - logger.trace(`GCodeFile with ID: ${id}:`, gcodeFile); - res.send(gcodeFile); - + logger.trace(`Returning GCode File contents with ID: ${id}:`); + + const filePath = path.join( + process.env.GCODE_STORAGE, + gcodeFile.gcodeFileName, + ); + + // Read the file + fs.readFile(filePath, "utf8", (err, data) => { + if (err) { + if (err.code === "ENOENT") { + // File not found + return res.status(404).send({ error: "File not found!" }); + } else { + // Other errors + return res.status(500).send({ error: "Error reading file." }); + } + } + + // Send the file contents in the response + res.send(data); + }); } catch (error) { logger.error("Error fetching GCodeFile:", error); res.status(500).send({ error: error.message }); @@ -63,19 +190,22 @@ export const editGCodeFileRouteHandler = async (req, res) => { // Fetch the gcodeFile with the given remote address const gcodeFile = await gcodeFileModel.findOne({ _id: id }); - if (!gcodeFile) { // Error handling + if (!gcodeFile) { + // Error handling logger.warn(`GCodeFile not found with supplied id.`); return res.status(404).send({ error: "Print job not found." }); } logger.trace(`GCodeFile with ID: ${id}:`, gcodeFile); - + try { - const { created_at, updated_at, started_at, status, ...updateData } = req.body; - + const { createdAt, updatedAt, started_at, status, ...updateData } = + req.body; + + console.log("Update data", updateData); const result = await gcodeFileModel.updateOne( { _id: id }, - { $set: updateData } + { $set: updateData }, ); if (result.nModified === 0) { logger.error("No gcodeFile updated."); @@ -86,8 +216,157 @@ export const editGCodeFileRouteHandler = async (req, res) => { res.status(500).send({ error: updateError.message }); } res.send("OK"); + } catch (fetchError) { + logger.error("Error fetching gcodeFile:", fetchError); + //res.status(500).send({ error: fetchError.message }); + } +}; + +export const newGCodeFileRouteHandler = async (req, res) => { + try { + let { ...newGCodeFile } = req.body; + newGCodeFile = { + ...newGCodeFile, + createdAt: new Date(), + updatedAt: new Date(), + }; + + const result = await gcodeFileModel.create(newGCodeFile); + if (result.nCreated === 0) { + logger.error("No gcode file created."); + res.status(500).send({ error: "No filament created." }); + } + res.status(200).send(result); + } catch (updateError) { + logger.error("Error updating filament:", updateError); + res.status(500).send({ error: updateError.message }); + } +}; + +export const parseGCodeFileHandler = async (req, res) => { + try { + // Use the same upload middleware as the uploadGCodeFileContentRouteHandler + gcodeUpload(req, res, async (err) => { + if (err) { + return res.status(500).send({ + error: err, + }); + } + + if (req.file == undefined) { + return res.send({ + message: "No file selected!", + }); + } + + try { + // Get the path to the uploaded file + const filePath = path.join(req.file.destination, req.file.filename); + + // Read the file content + const fileContent = fs.readFileSync(filePath, "utf8"); + + // Extract the config block + const configInfo = extractConfigBlock(fileContent); + + // Return the config as JSON + res.json(configInfo); + + // Optionally clean up the file after processing if it's not needed + fs.unlinkSync(filePath); + } catch (parseError) { + logger.error("Error parsing GCode file:", parseError); + res.status(500).send({ error: parseError.message }); + } + }); + } catch (error) { + logger.error("Error in parseGCodeFileHandler:", error); + res.status(500).send({ error: error.message }); + } +}; + +export const uploadGCodeFileContentRouteHandler = async (req, res) => { + try { + // Get ID from params + const id = new mongoose.Types.ObjectId(req.params.id); + // Fetch the gcodeFile with the given remote address + const gcodeFile = await gcodeFileModel.findOne({ _id: id }); + if (!gcodeFile) { + // Error handling + logger.warn(`GCodeFile not found with supplied id.`); + return res.status(404).send({ error: "Print job not found." }); + } + logger.trace(`GCodeFile with ID: ${id}`); + try { + gcodeUpload(req, res, async (err) => { + if (err) { + res.status(500).send({ + error: err, + }); + } else { + if (req.file == undefined) { + res.send({ + message: "No file selected!", + }); + } else { + // Get the path to the uploaded file + const filePath = path.join(req.file.destination, req.file.filename); + + // Read the file content + const fileContent = fs.readFileSync(filePath, "utf8"); + + // Update the gcodeFile document with the filename and the extracted config + const result = await gcodeFileModel.updateOne( + { _id: id }, + { + $set: { + gcodeFileName: req.file.filename, + }, + }, + ); + + if (result.nModified === 0) { + logger.error("No gcodeFile updated."); + res.status(500).send({ error: "No gcodeFiles updated." }); + } + + res.send({ + status: "OK", + file: `${req.file.filename}`, + }); + } + } + }); + } catch (updateError) { + logger.error("Error updating gcodeFile:", updateError); + res.status(500).send({ error: updateError.message }); + } } catch (fetchError) { logger.error("Error fetching gcodeFile:", fetchError); res.status(500).send({ error: fetchError.message }); } -}; \ No newline at end of file +}; + +export const getGCodeFileRouteHandler = async (req, res) => { + try { + // Get ID from params + const id = new mongoose.Types.ObjectId(req.params.id); + // Fetch the gcodeFile with the given remote address + const gcodeFile = await gcodeFileModel + .findOne({ + _id: id, + }) + .populate("filament"); + + if (!gcodeFile) { + logger.warn(`GCodeFile not found with supplied id.`); + return res.status(404).send({ error: "Print job not found." }); + } + + logger.trace(`GCodeFile with ID: ${id}:`); + res.send(gcodeFile); + } catch (error) { + logger.error("Error fetching GCodeFile:", error); + res.status(500).send({ error: error.message }); + } +}; diff --git a/src/services/printers/index.js b/src/services/printers/index.js index 67c9740..5ef7825 100644 --- a/src/services/printers/index.js +++ b/src/services/printers/index.js @@ -1,8 +1,5 @@ -import bcrypt from "bcrypt"; import dotenv from "dotenv"; -import { userModel } from "../../schemas/user.schema.js"; import { printerModel } from "../../schemas/printer.schema.js"; -import jwt from "jsonwebtoken"; import log4js from "log4js"; dotenv.config(); @@ -14,7 +11,7 @@ export const listPrintersRouteHandler = async ( req, res, page = 1, - limit = 25 + limit = 25, ) => { try { // Calculate the skip value based on the page number and limit @@ -32,18 +29,33 @@ export const listPrintersRouteHandler = async ( }; export const getPrinterRouteHandler = async (req, res) => { - const remoteAddress = req.params.remoteAddress; + const id = req.params.id; try { // Fetch the printer with the given remote address - const printer = await printerModel.findOne({ remoteAddress }); + const printer = await printerModel.findOne({ _id: id }) + .populate('subJobs') + .populate('currentJob') + .populate({ + path: 'currentJob', + populate: { + path: 'gcodeFile' + } + }) + .populate('currentSubJob') + .populate({ + path: 'subJobs', + populate: { + path: 'printJob' + } + }); if (!printer) { - logger.warn(`Printer with remote address ${remoteAddress} not found.`); + logger.warn(`Printer with id ${id} not found.`); return res.status(404).send({ error: "Printer not found" }); } - logger.trace(`Printer with remote address ${remoteAddress}:`, printer); + logger.trace(`Printer with id ${id}:`, printer); res.send(printer); } catch (error) { logger.error("Error fetching printer:", error); @@ -52,23 +64,13 @@ export const getPrinterRouteHandler = async (req, res) => { }; export const editPrinterRouteHandler = async (req, res) => { - const remoteAddress = req.params.remoteAddress; - const { friendlyName } = req.body; - + const id = req.params.id; try { - // Fetch the printer with the given remote address - const printer = await printerModel.findOne({ remoteAddress }); - - if (!printer) { - logger.warn(`Printer with remote address ${remoteAddress} not found.`); - return res.status(404).send({ error: "Printer not found" }); - } - - logger.trace(`Editing printer with remote address ${remoteAddress}:`, printer); + try { const result = await printerModel.updateOne( - { remoteAddress: remoteAddress }, - { $set: req.body } + { _id: id }, + { $set: req.body }, ); if (result.nModified === 0) { logger.error("No printers updated."); @@ -83,4 +85,52 @@ export const editPrinterRouteHandler = async (req, res) => { logger.error("Error fetching printer:", fetchError); res.status(500).send({ error: fetchError.message }); } -}; \ No newline at end of file +}; + +export const createPrinterRouteHandler = async (req, res) => { + try { + const { + printerName, + moonraker, + tags = [], + firmware = "n/a", + } = req.body; + + // Validate required fields + if (!printerName || !moonraker) { + logger.warn("Missing required fields in printer creation request"); + return res.status(400).send({ + error: "Missing required fields. printerName and moonraker configuration are required." + }); + } + + // Validate moonraker configuration + if (!moonraker.host || !moonraker.port || !moonraker.protocol) { + logger.warn("Invalid moonraker configuration in printer creation request"); + return res.status(400).send({ + error: "Invalid moonraker configuration. host, port, protocol are required." + }); + } + + // Create new printer instance + const newPrinter = new printerModel({ + printerName, + moonraker, + tags, + firmware, + online: false, + state: { + type: "offline" + } + }); + + // Save the printer + const savedPrinter = await newPrinter.save(); + + logger.info(`Created new printer: ${printerName}`); + res.status(201).send(savedPrinter); + } catch (error) { + logger.error("Error creating printer:", error); + res.status(500).send({ error: error.message }); + } +}; diff --git a/src/services/printjobs/index.js b/src/services/printjobs/index.js index e3fa925..29b3171 100644 --- a/src/services/printjobs/index.js +++ b/src/services/printjobs/index.js @@ -1,5 +1,7 @@ import dotenv from "dotenv"; -import { printJobModel } from "../../schemas/printjob.schema.js" +import mongoose from "mongoose"; +import { printJobModel } from "../../schemas/printjob.schema.js"; +import { printSubJobModel } from "../../schemas/printsubjob.schema.js"; import jwt from "jsonwebtoken"; import log4js from "log4js"; @@ -12,14 +14,20 @@ export const listPrintJobsRouteHandler = async ( req, res, page = 1, - limit = 25 + limit = 25, ) => { try { // Calculate the skip value based on the page number and limit const skip = (page - 1) * limit; // Fetch users with pagination - const printJobs = await printJobModel.find().skip(skip).limit(limit); + const printJobs = await printJobModel + .find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit) + .populate("subJobs", "state") + .populate("gcodeFile", "name"); logger.trace(`List of print jobs (Page ${page}, Limit ${limit}):`); res.send(printJobs); @@ -34,10 +42,14 @@ export const getPrintJobRouteHandler = async (req, res) => { // Get ID from params const id = new mongoose.Types.ObjectId(req.params.id); // Fetch the printJob with the given remote address - const printJob = await printJobModel.findOne({ - _id: id - }); - + const printJob = await printJobModel + .findOne({ + _id: id, + }) + .populate("printers", "printerName state") + .populate("gcodeFile") + .populate("subJobs"); + if (!printJob) { logger.warn(`PrintJob not found with supplied id.`); return res.status(404).send({ error: "Print job not found." }); @@ -55,34 +67,92 @@ export const editPrintJobRouteHandler = async (req, res) => { try { // Get ID from params const id = new mongoose.Types.ObjectId(req.params.id); + // Fetch the printJob with the given remote address const printJob = await printJobModel.findOne({ _id: id }); - if (!printJob) { // Error handling + if (!printJob) { logger.warn(`PrintJob not found with supplied id.`); return res.status(404).send({ error: "Print job not found." }); } logger.trace(`PrintJob with ID: ${id}:`, printJob); - - try { - const { created_at, updated_at, started_at, status, ...updateData } = req.body; - - const result = await printJobModel.updateOne( - { _id: id }, - { $set: updateData } - ); - if (result.nModified === 0) { - logger.error("No printJobs updated."); - res.status(500).send({ error: "No printJobs updated." }); - } - } catch (updateError) { - logger.error("Error updating printJob:", updateError); - res.status(500).send({ error: updateError.message }); + + const { createdAt, updatedAt, started_at, status, ...updateData } = + req.body; + + const result = await printJobModel.updateOne( + { _id: id }, + { $set: updateData }, + ); + + if (result.nModified === 0) { + logger.warn("No printJobs updated."); + return res.status(400).send({ error: "No printJobs updated." }); } - res.send("OK"); - } catch (fetchError) { - logger.error("Error fetching printJob:", fetchError); - res.status(500).send({ error: fetchError.message }); + + res.send({ message: "Print job updated successfully" }); + } catch (error) { + logger.error("Error updating printJob:", error); + res.status(500).send({ error: error.message }); } -}; \ No newline at end of file +}; + +export const createPrintJobRouteHandler = async (req, res) => { + try { + const { gcodeFile, printers, quantity = 1 } = req.body; + + if (!printers || printers.length === 0) { + return res + .status(400) + .send({ error: "At least one printer must be specified" }); + } + + // Convert printer IDs to ObjectIds + const printerIds = printers.map((id) => new mongoose.Types.ObjectId(id)); + + // Create new print job + const newPrintJob = new printJobModel({ + state: { type: "draft" }, + printers: printerIds, + gcodeFile: gcodeFile ? new mongoose.Types.ObjectId(gcodeFile) : null, + quantity, + subJobs: [], // Initialize empty array for subjob references + createdAt: new Date(), + updatedAt: new Date(), + startedAt: new Date(), + }); + + // Save the print job first to get its ID + const savedPrintJob = await newPrintJob.save(); + + // Create subjobs array with sequential numbers based on quantity + const subJobs = await Promise.all( + Array.from({ length: quantity }, (_, index) => { + const subJob = new printSubJobModel({ + printer: printerIds[index % printerIds.length], // Distribute across available printers + printJob: savedPrintJob._id, + gcodeFile: gcodeFile ? new mongoose.Types.ObjectId(gcodeFile) : null, + subJobId: `subjob-${index + 1}`, + state: { type: "draft" }, + number: index + 1, + createdAt: new Date(), + updatedAt: new Date(), + }); + return subJob.save(); + }), + ); + + // Update the print job with the subjob references + savedPrintJob.subJobs = subJobs.map((subJob) => subJob._id); + await savedPrintJob.save(); + + logger.trace( + `Created new print job with ID: ${savedPrintJob._id} and ${subJobs.length} subjobs`, + ); + res.status(201).send({ printJob: savedPrintJob, subJobs }); + } catch (error) { + logger.error("Error creating print job:", error); + res.status(500).send({ error: error.message }); + } +}; diff --git a/src/util/index.js b/src/util/index.js index 442f656..98dfcde 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -1,8 +1,251 @@ function parseStringIfNumber(input) { - if (typeof input === 'string' && !isNaN(input) && !isNaN(parseFloat(input))) { - return parseFloat(input); + if (typeof input === "string" && !isNaN(input) && !isNaN(parseFloat(input))) { + return parseFloat(input); } return input; } -export {parseStringIfNumber}; \ No newline at end of file +function convertToCamelCase(obj) { + const result = {}; + + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + const value = obj[key]; + + // Convert the key to camelCase + let camelKey = key + // First handle special cases with spaces, brackets and other characters + .replace(/\s*\[.*?\]\s*/g, "") // Remove brackets and their contents + .replace(/\s+/g, " ") // Normalize spaces + .trim() + // Split by common separators (space, underscore, hyphen) + .split(/[\s_-]/) + // Convert to camelCase + .map((word, index) => { + // Remove any non-alphanumeric characters + word = word.replace(/[^a-zA-Z0-9]/g, ""); + + // Lowercase first word, uppercase others + return index === 0 + ? word.toLowerCase() + : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + }) + .join(""); + + // Handle values that are objects recursively + if ( + value !== null && + typeof value === "object" && + !Array.isArray(value) + ) { + result[camelKey] = convertToCamelCase(value); + } else { + result[camelKey] = value; + } + } + } + + return result; +} + +function extractConfigBlock(fileContent, useCamelCase = true) { + const configObject = {}; + + // Extract header information + const headerBlockRegex = + /; HEADER_BLOCK_START([\s\S]*?)(?:; HEADER_BLOCK_END|$)/; + const headerBlockMatch = fileContent.match(headerBlockRegex); + if (headerBlockMatch && headerBlockMatch[1]) { + const headerLines = headerBlockMatch[1].split("\n"); + headerLines.forEach((line) => { + // Match lines with info after semicolon + const headerLineRegex = /^\s*;\s*([^:]+?):\s*(.*?)\s*$/; + const keyValueRegex = /^\s*;\s*([^:]+?):\s*(.*?)\s*$/; + const simpleValueRegex = /^\s*;\s*(.*?)\s*$/; + + // Try key-value format first + let match = line.match(keyValueRegex); + if (match) { + const key = match[1].trim(); + let value = match[2].trim(); + + // Try to convert value to appropriate type + if (!isNaN(value) && value !== "") { + value = Number(value); + } + configObject[key] = value; + } else { + // Try the simple format like "; generated by OrcaSlicer 2.1.1 on 2025-04-28 at 13:30:11" + match = line.match(simpleValueRegex); + if (match && match[1] && !match[1].includes("HEADER_BLOCK")) { + const text = match[1].trim(); + + // Extract slicer info + const slicerMatch = text.match( + /generated by (.*?) on (.*?) at (.*?)$/, + ); + if (slicerMatch) { + configObject["slicer"] = slicerMatch[1].trim(); + configObject["date"] = slicerMatch[2].trim(); + configObject["time"] = slicerMatch[3].trim(); + } else { + // Just add as a general header entry if it doesn't match any specific pattern + const key = `header_${Object.keys(configObject).length}`; + configObject[key] = text; + } + } + } + }); + } + + // Extract thumbnail data + const thumbnailBlockRegex = + /; THUMBNAIL_BLOCK_START([\s\S]*?)(?:; THUMBNAIL_BLOCK_END|$)/; + const thumbnailBlockMatch = fileContent.match(thumbnailBlockRegex); + if (thumbnailBlockMatch && thumbnailBlockMatch[1]) { + const thumbnailLines = thumbnailBlockMatch[1].split("\n"); + let base64Data = ""; + let thumbnailInfo = {}; + + thumbnailLines.forEach((line) => { + // Extract thumbnail dimensions and size from the line "thumbnail begin 640x640 27540" + const thumbnailHeaderRegex = /^\s*;\s*thumbnail begin (\d+)x(\d+) (\d+)/; + const match = line.match(thumbnailHeaderRegex); + + if (match) { + thumbnailInfo.width = parseInt(match[1], 10); + thumbnailInfo.height = parseInt(match[2], 10); + thumbnailInfo.size = parseInt(match[3], 10); + } else if ( + line.trim().startsWith("; ") && + !line.includes("THUMBNAIL_BLOCK") + ) { + // Collect base64 data (remove the leading semicolon and space and thumbnail end) + const dataLine = line.trim().substring(2); + if (dataLine && dataLine != "thumbnail end") { + base64Data += dataLine; + } + } + }); + + // Add thumbnail data to config object + if (base64Data) { + configObject.thumbnail = { + data: base64Data, + ...thumbnailInfo, + }; + } + } + + // Extract CONFIG_BLOCK + const configBlockRegex = + /; CONFIG_BLOCK_START([\s\S]*?)(?:; CONFIG_BLOCK_END|$)/; + const configBlockMatch = fileContent.match(configBlockRegex); + if (configBlockMatch && configBlockMatch[1]) { + // Extract each config line + const configLines = configBlockMatch[1].split("\n"); + // Process each line + configLines.forEach((line) => { + // Check if the line starts with a semicolon and has an equals sign + const configLineRegex = /^\s*;\s*([^=]+?)\s*=\s*(.*?)\s*$/; + const match = line.match(configLineRegex); + if (match) { + const key = match[1].trim(); + let value = match[2].trim(); + // Try to convert value to appropriate type + if (value === "true" || value === "false") { + value = value === "true"; + } else if (!isNaN(value) && value !== "") { + // Check if it's a number (but not a percentage) + if (!value.includes("%")) { + value = Number(value); + } + } + configObject[key] = value; + } + }); + } + + // Extract additional variables that appear after EXECUTABLE_BLOCK_END + const additionalVarsRegex = + /; EXECUTABLE_BLOCK_(?:START|END)([\s\S]*?)(?:; CONFIG_BLOCK_START|$)/i; + const additionalVarsMatch = fileContent.match(additionalVarsRegex); + if (additionalVarsMatch && additionalVarsMatch[1]) { + const additionalLines = additionalVarsMatch[1].split("\n"); + additionalLines.forEach((line) => { + // Match both standard format and the special case for "total filament cost" + const varRegex = + /^\s*;\s*((?:filament used|filament cost|total filament used|total filament cost|total layers count|estimated printing time)[^=]*?)\s*=\s*(.*?)\s*$/; + const match = line.match(varRegex); + if (match) { + const key = match[1].replace(/\[([^\]]+)\]/g, "$1").trim(); + let value = match[2].trim(); + // Clean up values - remove units in brackets and handle special cases + if (key.includes("filament used")) { + // Extract just the numeric value, ignoring units in brackets + const numMatch = value.match(/(\d+\.\d+)/); + if (numMatch) { + value = parseFloat(numMatch[1]); + } + } else if (key.includes("filament cost")) { + // Extract just the numeric value + const numMatch = value.match(/(\d+\.\d+)/); + if (numMatch) { + value = parseFloat(numMatch[1]); + } + } else if (key.includes("total layers count")) { + value = parseInt(value, 10); + } else if (key.includes("estimated printing time")) { + // Keep as string but trim any additional whitespace + value = value.trim(); + } + configObject[key] = value; + } + }); + } + + // Also extract extrusion width settings + const extrusionWidthRegex = /;\s*(.*?)\s*extrusion width\s*=\s*(.*?)mm/g; + let extrusionMatch; + while ((extrusionMatch = extrusionWidthRegex.exec(fileContent)) !== null) { + const settingName = extrusionMatch[1].trim(); + const settingValue = parseFloat(extrusionMatch[2].trim()); + configObject[`${settingName} extrusion width`] = settingValue; + } + + // Extract additional parameters after CONFIG_BLOCK_END if they exist + const postConfigParams = /; CONFIG_BLOCK_END\s*\n([\s\S]*?)$/; + const postConfigMatch = fileContent.match(postConfigParams); + if (postConfigMatch && postConfigMatch[1]) { + const postConfigLines = postConfigMatch[1].split("\n"); + postConfigLines.forEach((line) => { + // Match lines with format "; parameter_name = value" + const paramRegex = /^\s*;\s*([^=]+?)\s*=\s*(.*?)\s*$/; + const match = line.match(paramRegex); + if (match) { + const key = match[1].trim(); + let value = match[2].trim(); + + // Try to convert value to appropriate type + if (value === "true" || value === "false") { + value = value === "true"; + } else if (!isNaN(value) && value !== "") { + // Check if it's a number (but not a percentage) + if (!value.includes("%")) { + value = Number(value); + } + } + + // Add to config object if not already present + if (!configObject[key]) { + configObject[key] = value; + } + } + }); + } + + // Apply camelCase conversion if requested + return useCamelCase ? convertToCamelCase(configObject) : configObject; +} + +export { parseStringIfNumber, convertToCamelCase, extractConfigBlock };