diff --git a/csharp/App/Backend/db.sqlite b/csharp/App/Backend/db.sqlite index 55cc83a84..f344d58ad 100644 Binary files a/csharp/App/Backend/db.sqlite and b/csharp/App/Backend/db.sqlite differ diff --git a/typescript/Frontend/package-lock.json b/typescript/Frontend/package-lock.json index c02df11a5..d6a92a787 100644 --- a/typescript/Frontend/package-lock.json +++ b/typescript/Frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "@aws-sdk/client-s3": "^3.316.0", "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@minoru/react-dnd-treeview": "^3.4.1", @@ -26,6 +27,7 @@ "chart.js": "^4.2.1", "css-loader": "^6.7.3", "formik": "^2.2.9", + "linq-to-typescript": "^11.0.0", "package.json": "^2.0.1", "plotly.js": "^2.20.0", "react": "^18.2.0", @@ -39,8 +41,10 @@ "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", "reactflow": "^11.5.6", + "rxjs": "^7.8.0", "sass": "^1.58.3", "sass-loader": "^13.2.0", + "simplytyped": "^3.3.0", "style-loader": "^3.3.1", "testcafe": "^2.4.0", "typescript": "^4.9.5", @@ -69,6 +73,1293 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/crc32c": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", + "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32c/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", + "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/abort-controller": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.310.0.tgz", + "integrity": "sha512-v1zrRQxDLA1MdPim159Vx/CPHqsB4uybSxRi1CnfHO5ZjHryx3a5htW2gdGAykVCul40+yJXvfpufMrELVxH+g==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/chunked-blob-reader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.310.0.tgz", + "integrity": "sha512-CrJS3exo4mWaLnWxfCH+w88Ou0IcAZSIkk4QbmxiHl/5Dq705OLoxf4385MVyExpqpeVJYOYQ2WaD8i/pQZ2fg==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.316.0.tgz", + "integrity": "sha512-nmlvXf7c1Nz9VR/VlplgsZqPqe7eg5RYuuIb70rX/4Joe5x98sJqz72hyLblwQTB47lwPLK+qvHps9aVOY2QhA==", + "dependencies": { + "@aws-crypto/sha1-browser": "3.0.0", + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.316.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/eventstream-serde-browser": "3.310.0", + "@aws-sdk/eventstream-serde-config-resolver": "3.310.0", + "@aws-sdk/eventstream-serde-node": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-blob-browser": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/hash-stream-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/md5-js": "3.310.0", + "@aws-sdk/middleware-bucket-endpoint": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-expect-continue": "3.310.0", + "@aws-sdk/middleware-flexible-checksums": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-location-constraint": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-s3": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-ssec": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4-multi-region": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-stream-browser": "3.310.0", + "@aws-sdk/util-stream-node": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "@aws-sdk/util-waiter": "3.310.0", + "@aws-sdk/xml-builder": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.316.0.tgz", + "integrity": "sha512-wGXfIhR0lJGB8QTT0fwSwwklHePHxd2GW3IQt3trXnEYe0frmJ7vYRnVL5CSRKsikLDmaU7ll3SdsshMzQzo3w==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.316.0.tgz", + "integrity": "sha512-e2fvC7o42YV+LcZYfXCcvBn4L7NM9oNccnZ7T+pS6SFpHZlaqkw4uuQMRE6iUAof+Id7Mt7xDrz1x2yGlP+8GA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.316.0.tgz", + "integrity": "sha512-5SD59+DRVy1mKckGs/5J8OwWpRS3E5v4BX19XaX/s9JJ5Rw9aZd9DP4SZVpeNXztIPjkQSEzHgrUVlZFB1QJgg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-sts": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.310.0.tgz", + "integrity": "sha512-8vsT+/50lOqfDxka9m/rRt6oxv1WuGZoP8oPMk0Dt+TxXMbAzf4+rejBgiB96wshI1k3gLokYRjSQZn+dDtT8g==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.310.0.tgz", + "integrity": "sha512-vvIPQpI16fj95xwS7M3D48F7QhZJBnnCgB5lR+b7So+vsG9ibm1mZRVGzVpdxCvgyOhHFbvrby9aalNJmmIP1A==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.310.0.tgz", + "integrity": "sha512-baxK7Zp6dai5AGW01FIW27xS2KAaPUmKLIXv5SvFYsUgXXvNW55im4uG3b+2gA0F7V+hXvVBH08OEqmwW6we5w==", + "dependencies": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.316.0.tgz", + "integrity": "sha512-ZADkpdEjFCAXyzEpYbCRENlZ/AQEwevWdPd2yshjNo7xvOcepv4pPIBpYd8h9LvRafSLGA7zlWDz84hkIt+HKA==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.316.0.tgz", + "integrity": "sha512-oE1LTXP8XZp4bT8LhBeolMRiz0RwnmHDC2XpUmWO8LTmbDNrQO0mVzxEvXDLeKaN5BIFIJqNFlMgjWUMa9Kwcw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-ini": "3.316.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.310.0.tgz", + "integrity": "sha512-h73sg6GPMUWC+3zMCbA1nZ2O03nNJt7G96JdmnantiXBwHpRKWW8nBTLzx5uhXn6hTuTaoQRP/P+oxQJKYdMmA==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.316.0.tgz", + "integrity": "sha512-8/O2twlsoV1bDkZ9jd7JCMWsftfyoTyRT1UYscsKZGUDEgZRAxRkzS3GLYuLXEWNuxb1OB9rYk/cEJoxwy7T9g==", + "dependencies": { + "@aws-sdk/client-sso": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/token-providers": "3.316.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.310.0.tgz", + "integrity": "sha512-H4SzuZXILNhK6/IR1uVvsUDZvzc051hem7GLyYghBCu8mU+tq28YhKE8MfSroi6eL2e5Vujloij1OM2EQQkPkw==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-codec": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.310.0.tgz", + "integrity": "sha512-clIeSgWbZbxwtsxZ/yoedNM0/kJFSIjjHPikuDGhxhqc+vP6TN3oYyVMFrYwFaTFhk2+S5wZcWYMw8Op1pWo+A==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/eventstream-serde-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.310.0.tgz", + "integrity": "sha512-3S6ziuQVALgEyz0TANGtYDVeG8ArK4Y05mcgrs8qUTmsvlDIXX37cR/DvmVbNB76M4IrsZeSAIajL9644CywkA==", + "dependencies": { + "@aws-sdk/eventstream-serde-universal": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-serde-config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.310.0.tgz", + "integrity": "sha512-8s1Qdn9STj+sV75nUp9yt0W6fHS4BZ2jTm4Z/1Pcbvh2Gqs0WjH5n2StS+pDW5Y9J/HSGBl0ogmUr5lC5bXFHg==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-serde-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.310.0.tgz", + "integrity": "sha512-kSnRomCgW43K9TmQYuwN9+AoYPnhyOKroanUMyZEzJk7rpCPMj4OzaUpXfDYOvznFNYn7NLaH6nHLJAr0VPlJA==", + "dependencies": { + "@aws-sdk/eventstream-serde-universal": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-serde-universal": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.310.0.tgz", + "integrity": "sha512-Qyjt5k/waV5cDukpgT824ISZAz5U0pwzLz5ztR409u85AGNkF/9n7MS+LSyBUBSb0WJ5pUeSD47WBk+nLq9Nhw==", + "dependencies": { + "@aws-sdk/eventstream-codec": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.310.0.tgz", + "integrity": "sha512-Bi9vIwzdkw1zMcvi/zGzlWS9KfIEnAq4NNhsnCxbQ4OoIRU9wvU+WGZdBBhxg0ZxZmpp1j1aZhU53lLjA07MHw==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/hash-blob-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.310.0.tgz", + "integrity": "sha512-OoR8p0cbypToysLT0v3o2oyjy6+DKrY7GNCAzHOHJK9xmqXCt+DsjKoPeiY7o1sWX2aN6Plmvubj/zWxMKEn/A==", + "dependencies": { + "@aws-sdk/chunked-blob-reader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/hash-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.310.0.tgz", + "integrity": "sha512-NvE2fhRc8GRwCXBfDehxVAWCmVwVMILliAKVPAEr4yz2CkYs0tqU51S48x23dtna07H4qHtgpeNqVTthcIQOEQ==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/hash-stream-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-stream-node/-/hash-stream-node-3.310.0.tgz", + "integrity": "sha512-ZoXdybNgvMz1Hl6k/e32xVL3jmG5p2IEk5mTtLfFEuskTJ74Z+VMYKkkF1whyy7KQfH83H+TQGnsGtlRCchQKw==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/invalid-dependency": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.310.0.tgz", + "integrity": "sha512-1s5RG5rSPXoa/aZ/Kqr5U/7lqpx+Ry81GprQ2bxWqJvWQIJ0IRUwo5pk8XFxbKVr/2a+4lZT/c3OGoBOM1yRRA==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/md5-js": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/md5-js/-/md5-js-3.310.0.tgz", + "integrity": "sha512-x5sRBUrEfLWAS1EhwbbDQ7cXq6uvBxh3qR2XAsnGvFFceTeAadk7cVogWxlk3PC+OCeeym7c3/6Bv2HQ2f1YyQ==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.310.0.tgz", + "integrity": "sha512-uJJfHI7v4AgbJZRLtyI8ap2QRWkBokGc3iyUoQ+dVNT3/CE2ZCu694A6W+H0dRqg79dIE+f9CRNdtLGa/Ehhvg==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-content-length": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.310.0.tgz", + "integrity": "sha512-P8tQZxgDt6CAh1wd/W6WPzjc+uWPJwQkm+F7rAwRlM+k9q17HrhnksGDKcpuuLyIhPQYdmOMIkpKVgXGa4avhQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.310.0.tgz", + "integrity": "sha512-Z+N2vOL8K354/lstkClxLLsr6hCpVRh+0tCMXrVj66/NtKysCEZ/0b9LmqOwD9pWHNiI2mJqXwY0gxNlKAroUg==", + "dependencies": { + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.310.0.tgz", + "integrity": "sha512-l3d1z2gt+gINJDnPSyu84IxfzjzPfCQrqC1sunw2cZGo/sXtEiq698Q3SiTcO2PGP4LBQAy2RHb5wVBJP708CQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.310.0.tgz", + "integrity": "sha512-5ndnLgzgGVpWkmHBAiYkagHqiSuow8q62J4J6E2PzaQ77+fm8W3nfdy7hK5trHokEyouCZdxT/XK/IRhgj/4PA==", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-crypto/crc32c": "3.0.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.310.0.tgz", + "integrity": "sha512-QWSA+46/hXorXyWa61ic2K7qZzwHTiwfk2e9mRRjeIRepUgI3qxFjsYqrWtrOGBjmFmq0pYIY8Bb/DCJuQqcoA==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.310.0.tgz", + "integrity": "sha512-LFm0JTQWwTPWL/tZU2wsQTl8J5PpDEkXjEhaXVKamtyH0xhysRqd+0n92n65dc8oztAuQkb9xUbErGn5b6gsew==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.310.0.tgz", + "integrity": "sha512-Lurm8XofrASBRnAVtiSNuDSRsRqPNg27RIFLLsLp/pqog9nFJ0vz0kgdb9S5Z+zw83Mm+UlqOe6D8NTUNp4fVg==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.310.0.tgz", + "integrity": "sha512-SuB75/xk/gyue24gkriTwO2jFd7YcUGZDClQYuRejgbXSa3CO0lWyawQtfLcSSEBp9izrEVXuFH24K1eAft5nQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.310.0.tgz", + "integrity": "sha512-oTPsRy2W4s+dfxbJPW7Km+hHtv/OMsNsVfThAq8DDYKC13qlr1aAyOqGLD+dpBy2aKe7ss517Sy2HcHtHqm7/g==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/service-error-classification": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.310.0.tgz", + "integrity": "sha512-QK9x9g2ksg0hOjjYgqddeFcn5ctUEGdxJVu4OumPXceulefMcSO2jyH2qTybYSA93nqNQFdFmg5wQfvIRUWFCQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.310.0.tgz", + "integrity": "sha512-+5PFwlYNLvLLIfw0ASAoWV/iIF8Zv6R6QGtyP0CclhRSvNjgbQDVnV0g95MC5qvh+GB/Yjlkt8qAjLSPjHfsrQ==", + "dependencies": { + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-serde": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.310.0.tgz", + "integrity": "sha512-RNeeTVWSLTaentUeCgQKZhAl+C6hxtwD78cQWS10UymWpQFwbaxztzKUu4UQS5xA2j6PxwPRRUjqa4jcFjfLsg==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.310.0.tgz", + "integrity": "sha512-f9mKq+XMdW207Af3hKjdTnpNhdtwqWuvFs/ZyXoOkp/g1MY1O6L23Jy6i52m29LxbT4AuNRG1oKODfXM0vYVjQ==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.310.0.tgz", + "integrity": "sha512-CnEwNKVpd5bXnrCKPaePF8mWTA9ET21OMBb54y9b0fd8K02zoOcdBz4DWfh1SjFD4HkgCdja4egd8l2ivyvqmw==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.310.0.tgz", + "integrity": "sha512-010O1PD+UAcZVKRvqEusE1KJqN96wwrf6QsqbRM0ywsKQ21NDweaHvEDlds2VHpgmofxkRLRu/IDrlPkKRQrRg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.310.0.tgz", + "integrity": "sha512-x3IOwSwSbwKidlxRk3CNVHVUb06SRuaELxggCaR++QVI8NU6qD/l4VHXKVRvbTHiC/cYxXE/GaBDgQVpDR7V/g==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-endpoints": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.310.0.tgz", + "integrity": "sha512-T/Pp6htc6hq/Cq+MLNDSyiwWCMVF6GqbBbXKVlO5L8rdHx4sq9xPdoPveZhGWrxvkanjA6eCwUp6E0riBOSVng==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.310.0.tgz", + "integrity": "sha512-irv9mbcM9xC2xYjArQF5SYmHBMu4ciMWtGsoHII1nRuFOl9FoT4ffTvEPuLlfC6pznzvKt9zvnm6xXj7gDChKg==", + "dependencies": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/property-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.310.0.tgz", + "integrity": "sha512-3lxDb0akV6BBzmFe4nLPaoliQbAifyWJhuvuDOu7e8NzouvpQXs0275w9LePhhcgjKAEVXUIse05ZW2DLbxo/g==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.310.0.tgz", + "integrity": "sha512-fgZ1aw/irQtnrsR58pS8ThKOWo57Py3xX6giRvwSgZDEcxHfVzuQjy9yPuV++v04fdmdtgpbGf8WfvAAJ11yXQ==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.310.0.tgz", + "integrity": "sha512-ZHH8GV/80+pWGo7DzsvwvXR5xVxUHXUvPJPFAkhr6nCf78igdoF8gR10ScFoEKbtEapoNTaZlKHPXxpD8aPG7A==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.310.0.tgz", + "integrity": "sha512-YkIznoP6lsiIUHinx++/lbb3tlMURGGqMpo0Pnn32zYzGrJXA6eC3D0as2EcMjo55onTfuLcIiX4qzXes2MYOA==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/service-error-classification": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.310.0.tgz", + "integrity": "sha512-PuyC7k3qfIKeH2LCnDwbttMOKq3qAx4buvg0yfnJtQOz6t1AR8gsnAq0CjKXXyfkXwNKWTqCpE6lVNUIkXgsMw==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.310.0.tgz", + "integrity": "sha512-N0q9pG0xSjQwc690YQND5bofm+4nfUviQ/Ppgan2kU6aU0WUq8KwgHJBto/YEEI+VlrME30jZJnxtOvcZJc2XA==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.310.0.tgz", + "integrity": "sha512-1M60P1ZBNAjCFv9sYW29OF6okktaeibWyW3lMXqzoHF70lHBZh+838iUchznXUA5FLabfn4jBFWMRxlAXJUY2Q==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.310.0.tgz", + "integrity": "sha512-q8W+RIomTS/q85Ntgks/CoDElwqkC9+4OCicee5YznNHjQ4gtNWhUkYIyIRWRmXa/qx/AUreW9DM8FAecCOdng==", + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/signature-v4-crt": "^3.118.0" + }, + "peerDependenciesMeta": { + "@aws-sdk/signature-v4-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/smithy-client": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.316.0.tgz", + "integrity": "sha512-6YXOKbRnXeS8r8RWzuL6JMBolDYM5Wa4fD/VY6x/wK78i2xErHOvqzHgyyeLI1MMw4uqyd4wRNJNWC9TMPduXw==", + "dependencies": { + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.316.0.tgz", + "integrity": "sha512-foJ2YmB8A/mtp52riO2zdmBgzA3IpASNgUhY9FZg1BKpGcjqLQDGYP+BY3BA0H7CFsMa4PCf13M5wWwn1onyBA==", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.310.0.tgz", + "integrity": "sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/url-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.310.0.tgz", + "integrity": "sha512-mCLnCaSB9rQvAgx33u0DujLvr4d5yEm/W5r789GblwwQnlNXedVu50QRizMLTpltYWyAUoXjJgQnJHmJMaKXhw==", + "dependencies": { + "@aws-sdk/querystring-parser": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.310.0.tgz", + "integrity": "sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-base64": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-body-length-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-browser": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.316.0.tgz", + "integrity": "sha512-6FSqLhYmaihtH2n1s4b2rlLW0ABU8N6VZIfzLfe2ING4PF0MzfaMMhnTFUHVXfKCVGoR8yP6iyFTRCyHGVEL1w==", + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.316.0.tgz", + "integrity": "sha512-dkYy10hdjPSScXXvnjGpZpnJxllkb6ICHgLMwZ4JczLHhPM12T/4PQ758YN8HS+muiYDGX1Bl2z1jd/bMcewBQ==", + "dependencies": { + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.310.0.tgz", + "integrity": "sha512-zG+/d/O5KPmAaeOMPd6bW1abifdT0H03f42keLjYEoRZzYtHPC5DuPE0UayiWGckI6BCDgy0sRKXCYS49UNFaQ==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-middleware": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.310.0.tgz", + "integrity": "sha512-FTSUKL/eRb9X6uEZClrTe27QFXUNNp7fxYrPndZwk1hlaOP5ix+MIHBcI7pIiiY/JPfOUmPyZOu+HetlFXjWog==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.310.0.tgz", + "integrity": "sha512-FwWGhCBLfoivTMUHu1LIn4NjrN9JLJ/aX5aZmbcPIOhZVFJj638j0qDgZXyfvVqBuBZh7M8kGq0Oahy3dp69OA==", + "dependencies": { + "@aws-sdk/service-error-classification": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/util-stream-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-browser/-/util-stream-browser-3.310.0.tgz", + "integrity": "sha512-bysXZHwFwvbqOTCScCdCnoLk1K3GCo0HRIYEZuL7O7MHrQmfaYRXcaft/p22+GUv9VeFXS/eJJZ5r4u32az94w==", + "dependencies": { + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-stream-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-node/-/util-stream-node-3.310.0.tgz", + "integrity": "sha512-hueAXFK0GVvnfYFgqbF7587xZfMZff5jlIFZOHqx7XVU7bl7qrRUCnphHk8H6yZ7RoQbDPcfmHJgtEoAJg1T1Q==", + "dependencies": { + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.310.0.tgz", + "integrity": "sha512-yU/4QnHHuQ5z3vsUqMQVfYLbZGYwpYblPiuZx4Zo9+x0PBkNjYMqctdDcrpoH9Z2xZiDN16AmQGK1tix117ZKw==", + "dependencies": { + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.310.0.tgz", + "integrity": "sha512-Ra3pEl+Gn2BpeE7KiDGpi4zj7WJXZA5GXnGo3mjbi9+Y3zrbuhJAbdZO3mO/o7xDgMC6ph4xCTbaSGzU6b6EDg==", + "dependencies": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-waiter": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.310.0.tgz", + "integrity": "sha512-AV5j3guH/Y4REu+Qh3eXQU9igljHuU4XjX2sADAgf54C0kkhcCCkkiuzk3IsX089nyJCqIcj5idbjdvpnH88Vw==", + "dependencies": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.310.0.tgz", + "integrity": "sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -10301,6 +11592,21 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "node_modules/fast-xml-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -15220,6 +16526,14 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/linq-to-typescript": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/linq-to-typescript/-/linq-to-typescript-11.0.0.tgz", + "integrity": "sha512-6K1JHKDp90cLVk09xsTJn/ccw6YlEFxCC2iiL/4UmReZskrLCEi3JKU3iKkXspCY44vAzM95t49J13rdXQaCOw==", + "engines": { + "node": ">=17" + } + }, "node_modules/linux-platform-info": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/linux-platform-info/-/linux-platform-info-0.0.3.tgz", @@ -19542,6 +20856,14 @@ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -19908,6 +21230,14 @@ "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", "integrity": "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==" }, + "node_modules/simplytyped": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/simplytyped/-/simplytyped-3.3.0.tgz", + "integrity": "sha512-mz4RaNdKTZiaKXgi6P1k/cdsxV3gz+y1Wh2NXHWD40dExktLh4Xx/h6MFakmQWODZHj/2rKe59acacpL74ZhQA==", + "peerDependencies": { + "typescript": ">=2.8.0" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -20430,6 +21760,11 @@ "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==" + }, "node_modules/strongly-connected-components": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", @@ -23430,6 +24765,1089 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/crc32c": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", + "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "requires": { + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha1-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", + "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", + "requires": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "requires": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "requires": { + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "requires": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-sdk/abort-controller": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.310.0.tgz", + "integrity": "sha512-v1zrRQxDLA1MdPim159Vx/CPHqsB4uybSxRi1CnfHO5ZjHryx3a5htW2gdGAykVCul40+yJXvfpufMrELVxH+g==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/chunked-blob-reader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.310.0.tgz", + "integrity": "sha512-CrJS3exo4mWaLnWxfCH+w88Ou0IcAZSIkk4QbmxiHl/5Dq705OLoxf4385MVyExpqpeVJYOYQ2WaD8i/pQZ2fg==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-s3": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.316.0.tgz", + "integrity": "sha512-nmlvXf7c1Nz9VR/VlplgsZqPqe7eg5RYuuIb70rX/4Joe5x98sJqz72hyLblwQTB47lwPLK+qvHps9aVOY2QhA==", + "requires": { + "@aws-crypto/sha1-browser": "3.0.0", + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.316.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/eventstream-serde-browser": "3.310.0", + "@aws-sdk/eventstream-serde-config-resolver": "3.310.0", + "@aws-sdk/eventstream-serde-node": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-blob-browser": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/hash-stream-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/md5-js": "3.310.0", + "@aws-sdk/middleware-bucket-endpoint": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-expect-continue": "3.310.0", + "@aws-sdk/middleware-flexible-checksums": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-location-constraint": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-s3": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-ssec": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4-multi-region": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-stream-browser": "3.310.0", + "@aws-sdk/util-stream-node": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "@aws-sdk/util-waiter": "3.310.0", + "@aws-sdk/xml-builder": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.316.0.tgz", + "integrity": "sha512-wGXfIhR0lJGB8QTT0fwSwwklHePHxd2GW3IQt3trXnEYe0frmJ7vYRnVL5CSRKsikLDmaU7ll3SdsshMzQzo3w==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sso-oidc": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.316.0.tgz", + "integrity": "sha512-e2fvC7o42YV+LcZYfXCcvBn4L7NM9oNccnZ7T+pS6SFpHZlaqkw4uuQMRE6iUAof+Id7Mt7xDrz1x2yGlP+8GA==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sts": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.316.0.tgz", + "integrity": "sha512-5SD59+DRVy1mKckGs/5J8OwWpRS3E5v4BX19XaX/s9JJ5Rw9aZd9DP4SZVpeNXztIPjkQSEzHgrUVlZFB1QJgg==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-sts": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.310.0.tgz", + "integrity": "sha512-8vsT+/50lOqfDxka9m/rRt6oxv1WuGZoP8oPMk0Dt+TxXMbAzf4+rejBgiB96wshI1k3gLokYRjSQZn+dDtT8g==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.310.0.tgz", + "integrity": "sha512-vvIPQpI16fj95xwS7M3D48F7QhZJBnnCgB5lR+b7So+vsG9ibm1mZRVGzVpdxCvgyOhHFbvrby9aalNJmmIP1A==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-imds": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.310.0.tgz", + "integrity": "sha512-baxK7Zp6dai5AGW01FIW27xS2KAaPUmKLIXv5SvFYsUgXXvNW55im4uG3b+2gA0F7V+hXvVBH08OEqmwW6we5w==", + "requires": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.316.0.tgz", + "integrity": "sha512-ZADkpdEjFCAXyzEpYbCRENlZ/AQEwevWdPd2yshjNo7xvOcepv4pPIBpYd8h9LvRafSLGA7zlWDz84hkIt+HKA==", + "requires": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.316.0.tgz", + "integrity": "sha512-oE1LTXP8XZp4bT8LhBeolMRiz0RwnmHDC2XpUmWO8LTmbDNrQO0mVzxEvXDLeKaN5BIFIJqNFlMgjWUMa9Kwcw==", + "requires": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-ini": "3.316.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.310.0.tgz", + "integrity": "sha512-h73sg6GPMUWC+3zMCbA1nZ2O03nNJt7G96JdmnantiXBwHpRKWW8nBTLzx5uhXn6hTuTaoQRP/P+oxQJKYdMmA==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.316.0.tgz", + "integrity": "sha512-8/O2twlsoV1bDkZ9jd7JCMWsftfyoTyRT1UYscsKZGUDEgZRAxRkzS3GLYuLXEWNuxb1OB9rYk/cEJoxwy7T9g==", + "requires": { + "@aws-sdk/client-sso": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/token-providers": "3.316.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.310.0.tgz", + "integrity": "sha512-H4SzuZXILNhK6/IR1uVvsUDZvzc051hem7GLyYghBCu8mU+tq28YhKE8MfSroi6eL2e5Vujloij1OM2EQQkPkw==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-codec": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.310.0.tgz", + "integrity": "sha512-clIeSgWbZbxwtsxZ/yoedNM0/kJFSIjjHPikuDGhxhqc+vP6TN3oYyVMFrYwFaTFhk2+S5wZcWYMw8Op1pWo+A==", + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-serde-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.310.0.tgz", + "integrity": "sha512-3S6ziuQVALgEyz0TANGtYDVeG8ArK4Y05mcgrs8qUTmsvlDIXX37cR/DvmVbNB76M4IrsZeSAIajL9644CywkA==", + "requires": { + "@aws-sdk/eventstream-serde-universal": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-serde-config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.310.0.tgz", + "integrity": "sha512-8s1Qdn9STj+sV75nUp9yt0W6fHS4BZ2jTm4Z/1Pcbvh2Gqs0WjH5n2StS+pDW5Y9J/HSGBl0ogmUr5lC5bXFHg==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-serde-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.310.0.tgz", + "integrity": "sha512-kSnRomCgW43K9TmQYuwN9+AoYPnhyOKroanUMyZEzJk7rpCPMj4OzaUpXfDYOvznFNYn7NLaH6nHLJAr0VPlJA==", + "requires": { + "@aws-sdk/eventstream-serde-universal": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/eventstream-serde-universal": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.310.0.tgz", + "integrity": "sha512-Qyjt5k/waV5cDukpgT824ISZAz5U0pwzLz5ztR409u85AGNkF/9n7MS+LSyBUBSb0WJ5pUeSD47WBk+nLq9Nhw==", + "requires": { + "@aws-sdk/eventstream-codec": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/fetch-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.310.0.tgz", + "integrity": "sha512-Bi9vIwzdkw1zMcvi/zGzlWS9KfIEnAq4NNhsnCxbQ4OoIRU9wvU+WGZdBBhxg0ZxZmpp1j1aZhU53lLjA07MHw==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/hash-blob-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.310.0.tgz", + "integrity": "sha512-OoR8p0cbypToysLT0v3o2oyjy6+DKrY7GNCAzHOHJK9xmqXCt+DsjKoPeiY7o1sWX2aN6Plmvubj/zWxMKEn/A==", + "requires": { + "@aws-sdk/chunked-blob-reader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/hash-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.310.0.tgz", + "integrity": "sha512-NvE2fhRc8GRwCXBfDehxVAWCmVwVMILliAKVPAEr4yz2CkYs0tqU51S48x23dtna07H4qHtgpeNqVTthcIQOEQ==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/hash-stream-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-stream-node/-/hash-stream-node-3.310.0.tgz", + "integrity": "sha512-ZoXdybNgvMz1Hl6k/e32xVL3jmG5p2IEk5mTtLfFEuskTJ74Z+VMYKkkF1whyy7KQfH83H+TQGnsGtlRCchQKw==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/invalid-dependency": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.310.0.tgz", + "integrity": "sha512-1s5RG5rSPXoa/aZ/Kqr5U/7lqpx+Ry81GprQ2bxWqJvWQIJ0IRUwo5pk8XFxbKVr/2a+4lZT/c3OGoBOM1yRRA==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/is-array-buffer": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/md5-js": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/md5-js/-/md5-js-3.310.0.tgz", + "integrity": "sha512-x5sRBUrEfLWAS1EhwbbDQ7cXq6uvBxh3qR2XAsnGvFFceTeAadk7cVogWxlk3PC+OCeeym7c3/6Bv2HQ2f1YyQ==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-bucket-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.310.0.tgz", + "integrity": "sha512-uJJfHI7v4AgbJZRLtyI8ap2QRWkBokGc3iyUoQ+dVNT3/CE2ZCu694A6W+H0dRqg79dIE+f9CRNdtLGa/Ehhvg==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-content-length": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.310.0.tgz", + "integrity": "sha512-P8tQZxgDt6CAh1wd/W6WPzjc+uWPJwQkm+F7rAwRlM+k9q17HrhnksGDKcpuuLyIhPQYdmOMIkpKVgXGa4avhQ==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.310.0.tgz", + "integrity": "sha512-Z+N2vOL8K354/lstkClxLLsr6hCpVRh+0tCMXrVj66/NtKysCEZ/0b9LmqOwD9pWHNiI2mJqXwY0gxNlKAroUg==", + "requires": { + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-expect-continue": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.310.0.tgz", + "integrity": "sha512-l3d1z2gt+gINJDnPSyu84IxfzjzPfCQrqC1sunw2cZGo/sXtEiq698Q3SiTcO2PGP4LBQAy2RHb5wVBJP708CQ==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-flexible-checksums": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.310.0.tgz", + "integrity": "sha512-5ndnLgzgGVpWkmHBAiYkagHqiSuow8q62J4J6E2PzaQ77+fm8W3nfdy7hK5trHokEyouCZdxT/XK/IRhgj/4PA==", + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@aws-crypto/crc32c": "3.0.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.310.0.tgz", + "integrity": "sha512-QWSA+46/hXorXyWa61ic2K7qZzwHTiwfk2e9mRRjeIRepUgI3qxFjsYqrWtrOGBjmFmq0pYIY8Bb/DCJuQqcoA==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-location-constraint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.310.0.tgz", + "integrity": "sha512-LFm0JTQWwTPWL/tZU2wsQTl8J5PpDEkXjEhaXVKamtyH0xhysRqd+0n92n65dc8oztAuQkb9xUbErGn5b6gsew==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.310.0.tgz", + "integrity": "sha512-Lurm8XofrASBRnAVtiSNuDSRsRqPNg27RIFLLsLp/pqog9nFJ0vz0kgdb9S5Z+zw83Mm+UlqOe6D8NTUNp4fVg==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.310.0.tgz", + "integrity": "sha512-SuB75/xk/gyue24gkriTwO2jFd7YcUGZDClQYuRejgbXSa3CO0lWyawQtfLcSSEBp9izrEVXuFH24K1eAft5nQ==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.310.0.tgz", + "integrity": "sha512-oTPsRy2W4s+dfxbJPW7Km+hHtv/OMsNsVfThAq8DDYKC13qlr1aAyOqGLD+dpBy2aKe7ss517Sy2HcHtHqm7/g==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/service-error-classification": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + } + }, + "@aws-sdk/middleware-sdk-s3": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.310.0.tgz", + "integrity": "sha512-QK9x9g2ksg0hOjjYgqddeFcn5ctUEGdxJVu4OumPXceulefMcSO2jyH2qTybYSA93nqNQFdFmg5wQfvIRUWFCQ==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-arn-parser": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-sdk-sts": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.310.0.tgz", + "integrity": "sha512-+5PFwlYNLvLLIfw0ASAoWV/iIF8Zv6R6QGtyP0CclhRSvNjgbQDVnV0g95MC5qvh+GB/Yjlkt8qAjLSPjHfsrQ==", + "requires": { + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-serde": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.310.0.tgz", + "integrity": "sha512-RNeeTVWSLTaentUeCgQKZhAl+C6hxtwD78cQWS10UymWpQFwbaxztzKUu4UQS5xA2j6PxwPRRUjqa4jcFjfLsg==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-signing": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.310.0.tgz", + "integrity": "sha512-f9mKq+XMdW207Af3hKjdTnpNhdtwqWuvFs/ZyXoOkp/g1MY1O6L23Jy6i52m29LxbT4AuNRG1oKODfXM0vYVjQ==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-ssec": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.310.0.tgz", + "integrity": "sha512-CnEwNKVpd5bXnrCKPaePF8mWTA9ET21OMBb54y9b0fd8K02zoOcdBz4DWfh1SjFD4HkgCdja4egd8l2ivyvqmw==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-stack": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.310.0.tgz", + "integrity": "sha512-010O1PD+UAcZVKRvqEusE1KJqN96wwrf6QsqbRM0ywsKQ21NDweaHvEDlds2VHpgmofxkRLRu/IDrlPkKRQrRg==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.310.0.tgz", + "integrity": "sha512-x3IOwSwSbwKidlxRk3CNVHVUb06SRuaELxggCaR++QVI8NU6qD/l4VHXKVRvbTHiC/cYxXE/GaBDgQVpDR7V/g==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-endpoints": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/node-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.310.0.tgz", + "integrity": "sha512-T/Pp6htc6hq/Cq+MLNDSyiwWCMVF6GqbBbXKVlO5L8rdHx4sq9xPdoPveZhGWrxvkanjA6eCwUp6E0riBOSVng==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/node-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.310.0.tgz", + "integrity": "sha512-irv9mbcM9xC2xYjArQF5SYmHBMu4ciMWtGsoHII1nRuFOl9FoT4ffTvEPuLlfC6pznzvKt9zvnm6xXj7gDChKg==", + "requires": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/property-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.310.0.tgz", + "integrity": "sha512-3lxDb0akV6BBzmFe4nLPaoliQbAifyWJhuvuDOu7e8NzouvpQXs0275w9LePhhcgjKAEVXUIse05ZW2DLbxo/g==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/protocol-http": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.310.0.tgz", + "integrity": "sha512-fgZ1aw/irQtnrsR58pS8ThKOWo57Py3xX6giRvwSgZDEcxHfVzuQjy9yPuV++v04fdmdtgpbGf8WfvAAJ11yXQ==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/querystring-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.310.0.tgz", + "integrity": "sha512-ZHH8GV/80+pWGo7DzsvwvXR5xVxUHXUvPJPFAkhr6nCf78igdoF8gR10ScFoEKbtEapoNTaZlKHPXxpD8aPG7A==", + "requires": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/querystring-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.310.0.tgz", + "integrity": "sha512-YkIznoP6lsiIUHinx++/lbb3tlMURGGqMpo0Pnn32zYzGrJXA6eC3D0as2EcMjo55onTfuLcIiX4qzXes2MYOA==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/service-error-classification": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.310.0.tgz", + "integrity": "sha512-PuyC7k3qfIKeH2LCnDwbttMOKq3qAx4buvg0yfnJtQOz6t1AR8gsnAq0CjKXXyfkXwNKWTqCpE6lVNUIkXgsMw==" + }, + "@aws-sdk/shared-ini-file-loader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.310.0.tgz", + "integrity": "sha512-N0q9pG0xSjQwc690YQND5bofm+4nfUviQ/Ppgan2kU6aU0WUq8KwgHJBto/YEEI+VlrME30jZJnxtOvcZJc2XA==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/signature-v4": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.310.0.tgz", + "integrity": "sha512-1M60P1ZBNAjCFv9sYW29OF6okktaeibWyW3lMXqzoHF70lHBZh+838iUchznXUA5FLabfn4jBFWMRxlAXJUY2Q==", + "requires": { + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/signature-v4-multi-region": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.310.0.tgz", + "integrity": "sha512-q8W+RIomTS/q85Ntgks/CoDElwqkC9+4OCicee5YznNHjQ4gtNWhUkYIyIRWRmXa/qx/AUreW9DM8FAecCOdng==", + "requires": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/smithy-client": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.316.0.tgz", + "integrity": "sha512-6YXOKbRnXeS8r8RWzuL6JMBolDYM5Wa4fD/VY6x/wK78i2xErHOvqzHgyyeLI1MMw4uqyd4wRNJNWC9TMPduXw==", + "requires": { + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/token-providers": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.316.0.tgz", + "integrity": "sha512-foJ2YmB8A/mtp52riO2zdmBgzA3IpASNgUhY9FZg1BKpGcjqLQDGYP+BY3BA0H7CFsMa4PCf13M5wWwn1onyBA==", + "requires": { + "@aws-sdk/client-sso-oidc": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/types": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.310.0.tgz", + "integrity": "sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/url-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.310.0.tgz", + "integrity": "sha512-mCLnCaSB9rQvAgx33u0DujLvr4d5yEm/W5r789GblwwQnlNXedVu50QRizMLTpltYWyAUoXjJgQnJHmJMaKXhw==", + "requires": { + "@aws-sdk/querystring-parser": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-arn-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.310.0.tgz", + "integrity": "sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-base64": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", + "requires": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-body-length-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-body-length-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-buffer-from": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", + "requires": { + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-defaults-mode-browser": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.316.0.tgz", + "integrity": "sha512-6FSqLhYmaihtH2n1s4b2rlLW0ABU8N6VZIfzLfe2ING4PF0MzfaMMhnTFUHVXfKCVGoR8yP6iyFTRCyHGVEL1w==", + "requires": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-defaults-mode-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.316.0.tgz", + "integrity": "sha512-dkYy10hdjPSScXXvnjGpZpnJxllkb6ICHgLMwZ4JczLHhPM12T/4PQ758YN8HS+muiYDGX1Bl2z1jd/bMcewBQ==", + "requires": { + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.310.0.tgz", + "integrity": "sha512-zG+/d/O5KPmAaeOMPd6bW1abifdT0H03f42keLjYEoRZzYtHPC5DuPE0UayiWGckI6BCDgy0sRKXCYS49UNFaQ==", + "requires": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-hex-encoding": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-locate-window": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-middleware": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.310.0.tgz", + "integrity": "sha512-FTSUKL/eRb9X6uEZClrTe27QFXUNNp7fxYrPndZwk1hlaOP5ix+MIHBcI7pIiiY/JPfOUmPyZOu+HetlFXjWog==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.310.0.tgz", + "integrity": "sha512-FwWGhCBLfoivTMUHu1LIn4NjrN9JLJ/aX5aZmbcPIOhZVFJj638j0qDgZXyfvVqBuBZh7M8kGq0Oahy3dp69OA==", + "requires": { + "@aws-sdk/service-error-classification": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-stream-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-browser/-/util-stream-browser-3.310.0.tgz", + "integrity": "sha512-bysXZHwFwvbqOTCScCdCnoLk1K3GCo0HRIYEZuL7O7MHrQmfaYRXcaft/p22+GUv9VeFXS/eJJZ5r4u32az94w==", + "requires": { + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-stream-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream-node/-/util-stream-node-3.310.0.tgz", + "integrity": "sha512-hueAXFK0GVvnfYFgqbF7587xZfMZff5jlIFZOHqx7XVU7bl7qrRUCnphHk8H6yZ7RoQbDPcfmHJgtEoAJg1T1Q==", + "requires": { + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-uri-escape": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.310.0.tgz", + "integrity": "sha512-yU/4QnHHuQ5z3vsUqMQVfYLbZGYwpYblPiuZx4Zo9+x0PBkNjYMqctdDcrpoH9Z2xZiDN16AmQGK1tix117ZKw==", + "requires": { + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.310.0.tgz", + "integrity": "sha512-Ra3pEl+Gn2BpeE7KiDGpi4zj7WJXZA5GXnGo3mjbi9+Y3zrbuhJAbdZO3mO/o7xDgMC6ph4xCTbaSGzU6b6EDg==", + "requires": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", + "requires": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-waiter": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.310.0.tgz", + "integrity": "sha512-AV5j3guH/Y4REu+Qh3eXQU9igljHuU4XjX2sADAgf54C0kkhcCCkkiuzk3IsX089nyJCqIcj5idbjdvpnH88Vw==", + "requires": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/xml-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.310.0.tgz", + "integrity": "sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==", + "requires": { + "tslib": "^2.5.0" + } + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -31009,6 +33427,14 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "fast-xml-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", + "requires": { + "strnum": "^1.0.5" + } + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -34704,6 +37130,11 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "linq-to-typescript": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/linq-to-typescript/-/linq-to-typescript-11.0.0.tgz", + "integrity": "sha512-6K1JHKDp90cLVk09xsTJn/ccw6YlEFxCC2iiL/4UmReZskrLCEi3JKU3iKkXspCY44vAzM95t49J13rdXQaCOw==" + }, "linux-platform-info": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/linux-platform-info/-/linux-platform-info-0.0.3.tgz", @@ -37774,6 +40205,14 @@ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, + "rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "requires": { + "tslib": "^2.1.0" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -38049,6 +40488,12 @@ "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", "integrity": "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==" }, + "simplytyped": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/simplytyped/-/simplytyped-3.3.0.tgz", + "integrity": "sha512-mz4RaNdKTZiaKXgi6P1k/cdsxV3gz+y1Wh2NXHWD40dExktLh4Xx/h6MFakmQWODZHj/2rKe59acacpL74ZhQA==", + "requires": {} + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -38471,6 +40916,11 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "strongly-connected-components": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", diff --git a/typescript/Frontend/package.json b/typescript/Frontend/package.json index 7f6499cb8..6767b52e0 100644 --- a/typescript/Frontend/package.json +++ b/typescript/Frontend/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@aws-sdk/client-s3": "^3.316.0", "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@minoru/react-dnd-treeview": "^3.4.1", @@ -21,6 +22,7 @@ "chart.js": "^4.2.1", "css-loader": "^6.7.3", "formik": "^2.2.9", + "linq-to-typescript": "^11.0.0", "package.json": "^2.0.1", "plotly.js": "^2.20.0", "react": "^18.2.0", @@ -34,8 +36,10 @@ "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", "reactflow": "^11.5.6", + "rxjs": "^7.8.0", "sass": "^1.58.3", "sass-loader": "^13.2.0", + "simplytyped": "^3.3.0", "style-loader": "^3.3.1", "testcafe": "^2.4.0", "typescript": "^4.9.5", diff --git a/typescript/Frontend/src/App.tsx b/typescript/Frontend/src/App.tsx index 586cf423d..8847fb18a 100644 --- a/typescript/Frontend/src/App.tsx +++ b/typescript/Frontend/src/App.tsx @@ -14,12 +14,13 @@ import NavigationButtons from "./components/Layout/NavigationButtons"; import InstallationPage from "./components/Installations/InstallationPage"; import { UserContext } from "./components/Context/UserContextProvider"; import ResetPassword from "./ResetPassword"; +import innovenergyLogo from "./resources/innovenergy_Logo_onOrange.png"; const App = () => { const { token, setToken, removeToken } = useToken(); const [language, setLanguage] = useState("EN"); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); const getTranslations = () => { if (language === "DE") { @@ -31,7 +32,7 @@ const App = () => { if (!token) { return ; } - if (token && currentUser?.mustResetPassword) { + if (token && getCurrentUser()?.mustResetPassword) { return ; } return ( @@ -42,15 +43,20 @@ const App = () => { defaultLocale="EN" > - - - + + + innovenergy logo + + + + + { const [password, setPassword] = useState(""); const [loading, setLoading] = useState(false); const [error, setError] = useState(); - const { setCurrentUser } = useContext(UserContext); + const { saveCurrentUser } = useContext(UserContext); const verifyToken = async (token: string) => { axiosConfigWithoutToken.get("/GetAllInstallations", { @@ -39,7 +39,8 @@ const Login = ({ setToken, setLanguage }: I_LoginProps) => { verifyToken(data.token) .then(() => { setToken(data.token); - setCurrentUser(data.user); + saveCurrentUser(data.user); + console.log(data.user); setLoading(false); setLanguage(data.user.language); }) diff --git a/typescript/Frontend/src/ResetPassword.tsx b/typescript/Frontend/src/ResetPassword.tsx index c43db5d49..3b07db745 100644 --- a/typescript/Frontend/src/ResetPassword.tsx +++ b/typescript/Frontend/src/ResetPassword.tsx @@ -12,7 +12,7 @@ import * as Yup from "yup"; const ResetPassword = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(); - const { setCurrentUser } = useContext(UserContext); + const { saveCurrentUser } = useContext(UserContext); const validationSchema = Yup.object().shape({ password: Yup.string().required("*Password is required"), @@ -33,7 +33,8 @@ const ResetPassword = () => { params: { newPassword: formikValues.verifyPassword }, }) .then((res) => { - setCurrentUser(res.data); + saveCurrentUser(res.data); + console.log(res.data); setLoading(false); }) .catch((err) => setError(err)); diff --git a/typescript/Frontend/src/components/Context/UserContextProvider.tsx b/typescript/Frontend/src/components/Context/UserContextProvider.tsx index 9b18056d2..6b9f35adb 100644 --- a/typescript/Frontend/src/components/Context/UserContextProvider.tsx +++ b/typescript/Frontend/src/components/Context/UserContextProvider.tsx @@ -4,18 +4,36 @@ import { I_User } from "../../util/user.util"; interface InstallationContextProviderProps { currentUser?: I_User; setCurrentUser: (value: I_User) => void; + saveCurrentUser: (user: I_User) => void; + getCurrentUser: () => I_User; } export const UserContext = createContext({ currentUser: {} as I_User, setCurrentUser: () => {}, + saveCurrentUser: () => {}, + getCurrentUser: () => { + return {} as I_User; + }, }); const UserContextProvider = ({ children }: { children: ReactNode }) => { const [currentUser, setCurrentUser] = useState(); + const saveCurrentUser = (user: I_User) => { + localStorage.setItem("currentUser", JSON.stringify(user)); + setCurrentUser(user); + }; + + const getCurrentUser = (): I_User => { + const tokenString = localStorage.getItem("currentUser"); + return tokenString !== null ? JSON.parse(tokenString) : ""; + }; + return ( - + {children} ); diff --git a/typescript/Frontend/src/components/Context/UsersContextProvider.tsx b/typescript/Frontend/src/components/Context/UsersContextProvider.tsx index d6150b0e3..af77b5e9c 100644 --- a/typescript/Frontend/src/components/Context/UsersContextProvider.tsx +++ b/typescript/Frontend/src/components/Context/UsersContextProvider.tsx @@ -21,6 +21,7 @@ interface UsersContextProviderProps { getAvailableUsersForResource: () => I_User[]; fetchUsersWithInheritedAccessForResource: () => Promise; fetchUsersWithDirectAccessForResource: () => Promise; + fetchAvailableUsers: () => Promise; } export const UsersContext = createContext({ @@ -39,6 +40,9 @@ export const UsersContext = createContext({ fetchUsersWithDirectAccessForResource: () => { return Promise.resolve(); }, + fetchAvailableUsers: () => { + return Promise.resolve(); + }, }); const UsersContextProvider = ({ children }: { children: ReactNode }) => { @@ -51,7 +55,7 @@ const UsersContextProvider = ({ children }: { children: ReactNode }) => { const { currentType } = useContext(GroupContext); const { id } = useParams(); - const fetchUsers = useCallback( + const fetchUsersWithAccess = useCallback( async (route: string, setState: (value: any) => void) => { axiosConfig.get(route + currentType, { params: { id } }).then((res) => { setState(res.data); @@ -61,12 +65,15 @@ const UsersContextProvider = ({ children }: { children: ReactNode }) => { ); const fetchUsersWithInheritedAccessForResource = useCallback(async () => { - fetchUsers("/GetUsersWithInheritedAccessTo", setInheritedAccessUsers); - }, [fetchUsers]); + fetchUsersWithAccess( + "/GetUsersWithInheritedAccessTo", + setInheritedAccessUsers + ); + }, [fetchUsersWithAccess]); const fetchUsersWithDirectAccessForResource = useCallback(async () => { - fetchUsers("/GetUsersWithDirectAccessTo", setDirectAccessUsers); - }, [fetchUsers]); + fetchUsersWithAccess("/GetUsersWithDirectAccessTo", setDirectAccessUsers); + }, [fetchUsersWithAccess]); const getAvailableUsersForResource = () => { const inheritedUsers = inheritedAccessUsers.map( @@ -81,10 +88,15 @@ const UsersContextProvider = ({ children }: { children: ReactNode }) => { ); }; - useEffect(() => { - axiosConfig.get("/GetAllChildUsers").then((res) => { + const fetchAvailableUsers = async (): Promise => { + return axiosConfig.get("/GetAllChildUsers").then((res) => { + console.log(res); setAvailableUsers(res.data); }); + }; + + useEffect(() => { + fetchAvailableUsers(); }, []); return ( @@ -99,6 +111,7 @@ const UsersContextProvider = ({ children }: { children: ReactNode }) => { getAvailableUsersForResource, fetchUsersWithInheritedAccessForResource, fetchUsersWithDirectAccessForResource, + fetchAvailableUsers, }} > {children} diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx index 3cc3c7a98..733784022 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx @@ -8,13 +8,13 @@ import { useContext } from "react"; import { UserContext } from "../../Context/UserContextProvider"; const AccessManagement = () => { - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); return ( - {currentUser?.hasWriteAccess && } + {getCurrentUser().hasWriteAccess && } diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx index 96ed26adf..0fa267e85 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx @@ -59,15 +59,16 @@ const AvailableUserDialog = () => { scroll="paper" > - Grant access + option.name} + value={selectedUsers} renderInput={(params) => ( { sx={{ height: 40, ml: 2 }} onClick={handleGrant} > - + @@ -95,7 +99,7 @@ const AvailableUserDialog = () => { type="submit" onClick={() => setOpen(true)} > - + ); diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx index 791d354f5..aba3365f5 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx @@ -20,7 +20,7 @@ const UsersWithDirectAccess = () => { useContext(UsersContext); const { currentType } = useContext(GroupContext); const { id } = useParams(); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); useEffect(() => { fetchUsersWithDirectAccessForResource(); @@ -47,7 +47,7 @@ const UsersWithDirectAccess = () => { handleIconClick(user.id)} diff --git a/typescript/Frontend/src/components/Groups/FolderForm.tsx b/typescript/Frontend/src/components/Groups/FolderForm.tsx index 2796612e7..24c11f99e 100644 --- a/typescript/Frontend/src/components/Groups/FolderForm.tsx +++ b/typescript/Frontend/src/components/Groups/FolderForm.tsx @@ -23,13 +23,13 @@ const FolderForm = (props: I_CustomerFormProps) => { const { values, additionalButtons, handleSubmit } = props; const intl = useIntl(); const { fetchData } = useContext(GroupContext); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); const [snackbarOpen, setSnackbarOpen] = useState(false); const [error, setError] = useState(); const [loading, setLoading] = useState(false); - const readOnly = !currentUser?.hasWriteAccess; + const readOnly = !getCurrentUser().hasWriteAccess; const formik = useFormik({ initialValues: { diff --git a/typescript/Frontend/src/components/Installations/InstallationForm.tsx b/typescript/Frontend/src/components/Installations/InstallationForm.tsx index 52d628278..46681a51c 100644 --- a/typescript/Frontend/src/components/Installations/InstallationForm.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationForm.tsx @@ -9,6 +9,7 @@ import MoveDialog from "../Groups/Tree/MoveDialog"; import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import { UserContext } from "../Context/UserContextProvider"; +import * as Yup from "yup"; interface I_InstallationFormProps { values: I_Installation; @@ -19,11 +20,32 @@ const InstallationForm = (props: I_InstallationFormProps) => { const { values, id, hasMoveButton } = props; const [open, setOpen] = useState(false); - const intl = useIntl(); const { fetchData } = useContext(InstallationContext); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); - const readOnly = !currentUser?.hasWriteAccess; + const readOnly = !getCurrentUser().hasWriteAccess; + const intl = useIntl(); + + const validationSchema = Yup.object().shape({ + name: Yup.string().required( + intl.formatMessage({ + id: "requiredName", + defaultMessage: "Name is required", + }) + ), + region: Yup.string().required( + intl.formatMessage({ + id: "requiredRegion", + defaultMessage: "Region is required", + }) + ), + location: Yup.string().required( + intl.formatMessage({ + id: "requiredLocation", + defaultMessage: "Location is required", + }) + ), + }); const formik = useFormik({ initialValues: { @@ -44,6 +66,7 @@ const InstallationForm = (props: I_InstallationFormProps) => { fetchData(); }); }, + validationSchema, }); const handleClose = () => { @@ -62,6 +85,10 @@ const InstallationForm = (props: I_InstallationFormProps) => { name="name" value={formik.values.name} handleChange={formik.handleChange} + helperText={ + formik.errors.name && formik.touched.name ? formik.errors.name : "" + } + error={!!formik.errors.name && formik.touched.name} /> { name="region" value={formik.values.region} handleChange={formik.handleChange} + helperText={ + formik.errors.region && formik.touched.region + ? formik.errors.region + : "" + } + error={!!formik.errors.region && formik.touched.region} /> { name="location" value={formik.values.location} handleChange={formik.handleChange} + helperText={ + formik.errors.location && formik.touched.location + ? formik.errors.location + : "" + } + error={!!formik.errors.location && formik.touched.location} /> { bgcolor: "background.paper", position: "relative", overflow: "auto", - maxHeight: 400, py: 0, mt: 1, + height: "80vh", + maxHeight: "70vh", }} component="nav" aria-labelledby="nested-list-subheader" diff --git a/typescript/Frontend/src/components/Installations/InstallationPage.tsx b/typescript/Frontend/src/components/Installations/InstallationPage.tsx index 7650d1d67..4203a5d0e 100644 --- a/typescript/Frontend/src/components/Installations/InstallationPage.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationPage.tsx @@ -8,7 +8,7 @@ import { Grid } from "@mui/material"; const InstallationPage = () => { return ( <> - + diff --git a/typescript/Frontend/src/components/Installations/Installations.tsx b/typescript/Frontend/src/components/Installations/Installations.tsx index 4a09e0221..b79f34d9b 100644 --- a/typescript/Frontend/src/components/Installations/Installations.tsx +++ b/typescript/Frontend/src/components/Installations/Installations.tsx @@ -12,7 +12,7 @@ import Installation from "./Installation"; const Installations = () => { return ( - + { - return ; + const [timeSeries, setTimeSeries] = useState([]); + const s3Access = new S3Access( + "saliomameiringen", + "sos-ch-dk-2", + "exo.io", + "EXO18e7ae9e53fae71ee55cf35b", + "3Cyonq8gMQ0a3elTH2vP7Yv-czcCj8iE2lBcPB9XhSc", + "" + ); + + const fetchData = ( + timestamp: UnixTime + ): Promise>> => { + const s3Path = `${timestamp.ticks}.csv`; + return s3Access + .get(s3Path) + .then(async (r) => { + if (r.status === 404) { + return Promise.resolve(FetchResult.notAvailable); + } else if (r.status === 200) { + const text = await r.text(); + return parseCsv(text); + } else { + console.error("unexpected status code"); + return Promise.resolve(FetchResult.notAvailable); + } + }) + .catch((e) => { + console.log(e); + return Promise.resolve(FetchResult.tryLater); + }); + }; + + const cache = new DataCache(fetchData, TimeSpan.fromSeconds(2)); + + const sampleTimes = UnixTime.fromTicks(1682085650) + .earlier(TimeSpan.fromMinutes(1)) + .rangeBefore(TimeSpan.fromMinutes(1)) + .sample(TimeSpan.fromSeconds(2)); + + cache.getSeries(sampleTimes); + + const update = cache.gotData.pipe( + debounceTime(2000), + map((_) => setTimeSeries(cache.getSeries(sampleTimes))) + ); + + update.subscribe(); + + return ( + <> + + + ); }; export default Log; diff --git a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx index 7a6fa4b87..69e6e5218 100644 --- a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx +++ b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx @@ -1,59 +1,15 @@ import Plot from "react-plotly.js"; -import { TimeSeries, timeSeries } from "../ExampleLogData"; +import { RecordSeries } from "../../../dataCache/data"; +import { transformToGraphData } from "../../../util/graph.util"; -const ScalarGraph = () => { - const transformToGraphData = (timeStampData: TimeSeries) => { - return Object.keys(timeSeries).reduce( - (timeSeriesAcc, timeSeriesKey) => { - const logData = timeStampData[parseInt(timeSeriesKey)]; - const transformedTimeSeries = Object.keys(logData).reduce( - (logAcc, logDataKey) => { - return { - ...logAcc, - [logDataKey]: { - [timeSeriesKey]: logData[logDataKey], - }, - }; - }, - {} as any - ); - Object.keys(transformedTimeSeries).forEach( - (transformedTimeSeriesKey) => { - const date = new Date(parseInt(timeSeriesKey)); - if (timeSeriesAcc[transformedTimeSeriesKey]) { - timeSeriesAcc[transformedTimeSeriesKey] = { - x: [...timeSeriesAcc[transformedTimeSeriesKey].x, date], - y: [ - ...timeSeriesAcc[transformedTimeSeriesKey].y, - transformedTimeSeries[transformedTimeSeriesKey][ - timeSeriesKey - ], - ], - }; - } else { - timeSeriesAcc[transformedTimeSeriesKey] = { - x: [date], - y: [ - transformedTimeSeries[transformedTimeSeriesKey][ - timeSeriesKey - ], - ], - }; - } - } - ); - return timeSeriesAcc; - }, - {} as { - [path: string]: { x: Date[]; y: number[] }; - } - ); - }; +interface I_ScalarGraphProps { + data: RecordSeries; +} +const ScalarGraph = (props: I_ScalarGraphProps) => { const renderGraphs = () => { - const coordinateTimeSeries = transformToGraphData(timeSeries); + const coordinateTimeSeries = transformToGraphData(props.data); return Object.keys(coordinateTimeSeries).map((path) => { - console.log(timeSeries[parseInt(path)]); return ( { ], }} onUpdate={(figure) => { - console.log(figure); + //console.log(figure); }} /> ); diff --git a/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx b/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx index cb208092f..d94b24430 100644 --- a/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx +++ b/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx @@ -9,6 +9,8 @@ interface I_InnovenergyTextfieldProps { type?: string; readOnly?: boolean; disabled?: boolean; + helperText?: string; + error?: boolean; } const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => { @@ -30,6 +32,9 @@ const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => { "-webkit-text-fill-color": "black", color: "black", }, + ".MuiFormHelperText-root": { + marginLeft: 0, + }, }} value={props.value || ""} onChange={props.handleChange} @@ -37,6 +42,8 @@ const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => { readOnly: props.readOnly, }} disabled={props.disabled} + helperText={props.helperText} + error={props.error} /> diff --git a/typescript/Frontend/src/components/Layout/Search.tsx b/typescript/Frontend/src/components/Layout/Search.tsx index effd13eed..52bc74d1f 100644 --- a/typescript/Frontend/src/components/Layout/Search.tsx +++ b/typescript/Frontend/src/components/Layout/Search.tsx @@ -12,7 +12,7 @@ const SearchSidebar = (props: SearchSidebarProps) => { const intl = useIntl(); return ( - <> +
{ onChange={(e) => setSearchQuery(e.target.value)} /> - +
); }; diff --git a/typescript/Frontend/src/components/Users/User.tsx b/typescript/Frontend/src/components/Users/User.tsx index c18572f00..7c6e3ee8d 100644 --- a/typescript/Frontend/src/components/Users/User.tsx +++ b/typescript/Frontend/src/components/Users/User.tsx @@ -7,10 +7,10 @@ import { I_User } from "../../util/user.util"; import UserForm from "./UserForm"; import { useIntl } from "react-intl"; -interface I_DetailProps { +interface I_DetailProps { hasMoveButton?: boolean; } -const Detail = (props: I_DetailProps) => { +const Detail = (props: I_DetailProps) => { const { id } = useParams(); const { locale } = useIntl(); const [values, setValues] = useState(); diff --git a/typescript/Frontend/src/components/Users/UserForm.tsx b/typescript/Frontend/src/components/Users/UserForm.tsx index 8fb9e5f6e..d7ab9f5ec 100644 --- a/typescript/Frontend/src/components/Users/UserForm.tsx +++ b/typescript/Frontend/src/components/Users/UserForm.tsx @@ -7,6 +7,7 @@ import { I_User } from "../../util/user.util"; import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import { UserContext } from "../Context/UserContextProvider"; +import { UsersContext } from "../Context/UsersContextProvider"; interface I_UserFormProps { handleSubmit: (formikValues: Partial) => Promise; @@ -19,9 +20,11 @@ const UserForm = (props: I_UserFormProps) => { const [error, setError] = useState(); const intl = useIntl(); - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); + const { fetchAvailableUsers } = useContext(UsersContext); - const readOnly = !currentUser?.hasWriteAccess; + const currentUser = getCurrentUser(); + const readOnly = !currentUser.hasWriteAccess; const formik = useFormik({ initialValues: { @@ -34,6 +37,7 @@ const UserForm = (props: I_UserFormProps) => { handleSubmit(formikValues) .then(() => { setOpen(true); + fetchAvailableUsers(); setLoading(false); }) .catch((err: AxiosError) => { @@ -85,7 +89,7 @@ const UserForm = (props: I_UserFormProps) => { /> {loading && } - {currentUser?.hasWriteAccess && ( + {currentUser.hasWriteAccess && ( diff --git a/typescript/Frontend/src/components/Users/UserTabs.tsx b/typescript/Frontend/src/components/Users/UserTabs.tsx index c44fac79b..6a3068d02 100644 --- a/typescript/Frontend/src/components/Users/UserTabs.tsx +++ b/typescript/Frontend/src/components/Users/UserTabs.tsx @@ -14,26 +14,22 @@ const UserTabs = () => { if (id) { return ( - - - - - - - + + + ); } return null; diff --git a/typescript/Frontend/src/components/Users/Users.tsx b/typescript/Frontend/src/components/Users/Users.tsx index 03cc73db2..accfdbf31 100644 --- a/typescript/Frontend/src/components/Users/Users.tsx +++ b/typescript/Frontend/src/components/Users/Users.tsx @@ -11,13 +11,13 @@ import { useContext } from "react"; import { UserContext } from "../Context/UserContextProvider"; const Users = () => { - const { currentUser } = useContext(UserContext); + const { getCurrentUser } = useContext(UserContext); return ( - + - {currentUser?.hasWriteAccess && } + {getCurrentUser().hasWriteAccess && } diff --git a/typescript/Frontend/src/config/axiosConfig.tsx b/typescript/Frontend/src/config/axiosConfig.tsx index f71357136..38e7e1f81 100644 --- a/typescript/Frontend/src/config/axiosConfig.tsx +++ b/typescript/Frontend/src/config/axiosConfig.tsx @@ -10,7 +10,7 @@ const axiosConfig = axios.create({ axiosConfig.defaults.params = {}; axiosConfig.interceptors.request.use( (config) => { - const tokenString = sessionStorage.getItem("token"); + const tokenString = localStorage.getItem("token"); const token = tokenString !== null ? JSON.parse(tokenString) : ""; if (token) { config.params["authToken"] = token; diff --git a/typescript/Frontend/src/dataCache/S3/S3Access.ts b/typescript/Frontend/src/dataCache/S3/S3Access.ts new file mode 100644 index 000000000..6780f0ced --- /dev/null +++ b/typescript/Frontend/src/dataCache/S3/S3Access.ts @@ -0,0 +1,79 @@ +import {sha1Hmac} from "./Sha1"; +import {Utf8} from "./Utf8"; +import {toBase64} from "./UInt8Utils"; + +export class S3Access +{ + constructor + ( + readonly bucket: string, + readonly region: string, + readonly provider: string, + readonly key: string, + readonly secret: string, + readonly contentType: string + ) + {} + + get host() : string { return `${this.bucket}.${this.region}.${this.provider}` } + get url() : string { return `https://${this.host}` } + + public get(s3Path : string): Promise + { + const method = "GET"; + const auth = this.createAuthorizationHeader(method, s3Path, ""); + const url = this.url + "/" + s3Path + const headers = {"Host": this.host, "Authorization": auth}; + + try + { + return fetch(url, {method: method, mode: "cors", headers: headers}) + } + catch + { + return Promise.reject() + } + } + + private createAuthorizationHeader(method: string, + s3Path: string, + date: string) + { + return createAuthorizationHeader + ( + method, + this.bucket, + s3Path, + date, + this.key, + this.secret, + this.contentType + ); + } +} + +function createAuthorizationHeader(method: string, + bucket: string, + s3Path: string, + date: string, + s3Key: string, + s3Secret: string, + contentType: string, + md5Hash: string = "") +{ + // StringToSign = HTTP-Verb + "\n" + + // Content-MD5 + "\n" + + // Content-Type + "\n" + + // Date + "\n" + + // CanonicalizedAmzHeaders + + // CanonicalizedResource; + + const payload = Utf8.encode(`${method}\n${md5Hash}\n${contentType}\n${date}\n/${bucket}/${s3Path}`) + + //console.log(`${method}\n${md5Hash}\n${contentType}\n${date}\n/${bucket}/${s3Path}`) + + const secret = Utf8.encode(s3Secret) + const signature = toBase64(sha1Hmac(payload, secret)); + + return `AWS ${s3Key}:${signature}` +} diff --git a/typescript/Frontend/src/dataCache/S3/Sha1.ts b/typescript/Frontend/src/dataCache/S3/Sha1.ts new file mode 100644 index 000000000..17dcea21f --- /dev/null +++ b/typescript/Frontend/src/dataCache/S3/Sha1.ts @@ -0,0 +1,125 @@ +import {concat, pad} from "./UInt8Utils"; + +const BigEndian = false + +export function sha1Hmac(msg: Uint8Array, key: Uint8Array): Uint8Array +{ + if (key.byteLength > 64) + key = sha1(key) + if (key.byteLength < 64) + key = pad(key, 64) + + const oKey = key.map(b => b ^ 0x5C); + const iKey = key.map(b => b ^ 0x36); + + const iData = concat(iKey, msg); + const iHash = sha1(iData); + const oData = concat(oKey, iHash); + + return sha1(oData); +} + +export function sha1(data: Uint8Array): Uint8Array +{ + const paddedData: DataView = initData(data) + + const H = new Uint32Array([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]) + const S = new Uint32Array(5) // State + const round = new Uint32Array(80); + + function initRound(startOffset: number) + { + for (let i = 0; i < 16; i++) + round[i] = paddedData.getUint32((startOffset + i) * 4, BigEndian); + + for (let i = 16; i < 80; i++) + { + const int32 = round[i - 3] ^ round[i - 8] ^ round[i - 14] ^ round[i - 16]; + round[i] = rotate1(int32); // SHA0 has no rotate + } + } + + const functions = + [ + () => (S[1] & S[2] | ~S[1] & S[3]) + 0x5A827999, + () => (S[1] ^ S[2] ^ S[3]) + 0x6ED9EBA1, + () => (S[1] & S[2] | S[1] & S[3] | S[2] & S[3]) + 0x8F1BBCDC, + () => (S[1] ^ S[2] ^ S[3]) + 0xCA62C1D6 + ] + + for (let startOffset = 0; startOffset < paddedData.byteLength / 4; startOffset += 16) + { + initRound(startOffset); + + S.set(H) + + for (let r = 0, i = 0; r < 4; r++) + { + const f = functions[r] + const end = i + 20; + + do + { + const S0 = rotate5(S[0]) + f() + S[4] + round[i]; + S[4] = S[3]; + S[3] = S[2]; + S[2] = rotate30(S[1]); + S[1] = S[0]; + S[0] = S0; + } + while (++i < end) + } + + for (let i = 0; i < 5; i++) + H[i] += S[i] + } + + swapEndianness(H); + + return new Uint8Array(H.buffer) +} + +function rotate5(int32: number) +{ + return (int32 << 5) | (int32 >>> 27); // >>> for unsigned shift +} + +function rotate30(int32: number) +{ + return (int32 << 30) | (int32 >>> 2); +} + +function rotate1(int32: number) +{ + return (int32 << 1) | (int32 >>> 31); +} + +function initData(data: Uint8Array): DataView +{ + const dataLength = data.length + const extendedLength = dataLength + 9; // add 8 bytes for UInt64 length + 1 byte for "stop-bit" (0x80) + const paddedLength = Math.ceil(extendedLength / 64) * 64; // pad to 512 bits block + const paddedData = new Uint8Array(paddedLength) + + paddedData.set(data) + paddedData[dataLength] = 0x80 // append single 1 bit at end of data + + const dataView = new DataView(paddedData.buffer) + + // append UInt64 length + dataView.setUint32(paddedData.length - 4, dataLength << 3 , BigEndian) // dataLength in *bits* LO, (<< 3: x8 bits per byte) + dataView.setUint32(paddedData.length - 8, dataLength >>> 29, BigEndian) // dataLength in *bits* HI + + return dataView +} + +function swapEndianness(uint32Array: Uint32Array) +{ + const dv = new DataView(uint32Array.buffer) + for (let i = 0; i < uint32Array.byteLength; i += 4) + { + const uint32 = dv.getUint32(i, false) + dv.setUint32(i, uint32, true) + } +} + diff --git a/typescript/Frontend/src/dataCache/S3/UInt8Utils.ts b/typescript/Frontend/src/dataCache/S3/UInt8Utils.ts new file mode 100644 index 000000000..7e6c5d607 --- /dev/null +++ b/typescript/Frontend/src/dataCache/S3/UInt8Utils.ts @@ -0,0 +1,56 @@ +export function pad(data: Uint8Array, length: number): Uint8Array +{ + if (length < data.byteLength) + throw new RangeError("length") + + const padded = new Uint8Array(length) + padded.set(data) + + return padded; +} + +export function concat(left: Uint8Array, right: Uint8Array): Uint8Array +{ + const c = new Uint8Array(left.length + right.length); + c.set(left); + c.set(right, left.length); + return c +} + +export function toHexString(data: Uint8Array) +{ + return [...data].map(byteToHex).join(''); +} + +function byteToHex(b: number) +{ + return b.toString(16).padStart(2, "0"); +} + +const b64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +export function toBase64(data : Uint8Array) : string +{ + const byteLength = data.byteLength + const base64LengthPadded = 4 * Math.ceil(byteLength / 3) + const base64Length = Math.ceil(byteLength / 3 * 4); + + const base64 = new Array(base64LengthPadded) + + for (let i = 0, o = 0; i < byteLength;) + { + const x = data[i++] + const y = data[i++] ?? 0 + const z = data[i++] ?? 0 + + base64[o++] = b64Chars[x >>> 2] + base64[o++] = b64Chars[(x << 4 | y >>> 4) & 63] + base64[o++] = b64Chars[(y << 2 | z >>> 6) & 63] + base64[o++] = b64Chars[z & 63] + } + + for (let i = base64LengthPadded; i > base64Length ;) + base64[--i] = "=" + + return base64.join('') +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/S3/Utf8.ts b/typescript/Frontend/src/dataCache/S3/Utf8.ts new file mode 100644 index 000000000..a7ea2951d --- /dev/null +++ b/typescript/Frontend/src/dataCache/S3/Utf8.ts @@ -0,0 +1,10 @@ +export namespace Utf8 +{ + const encoder = new TextEncoder() + const decoder = new TextDecoder() + + export const encode = (text: string): Uint8Array => encoder.encode(text); + export const decode = (data: Uint8Array): string => decoder.decode(data); +} + + diff --git a/typescript/Frontend/src/dataCache/data.ts b/typescript/Frontend/src/dataCache/data.ts new file mode 100644 index 000000000..8fdb168e1 --- /dev/null +++ b/typescript/Frontend/src/dataCache/data.ts @@ -0,0 +1,21 @@ +import { Maybe } from "yup"; +import {Timestamped} from "./types"; +import { isDefined } from "./utils/maybe"; + +export type DataRecord = Record + +export type DataPoint = Timestamped> +export type RecordSeries = Array +export type PointSeries = Array>> +export type DataSeries = Array> + +export function getPoints(recordSeries: RecordSeries, series: keyof DataRecord): PointSeries +{ + return recordSeries.map(p => ({time: p.time, value: isDefined(p.value) ? p.value[series] : undefined})) +} + +export function getData(recordSeries: RecordSeries, series: keyof DataRecord): DataSeries +{ + return recordSeries.map(p => (isDefined(p.value) ? p.value[series] : undefined)) +} + diff --git a/typescript/Frontend/src/dataCache/dataCache.ts b/typescript/Frontend/src/dataCache/dataCache.ts new file mode 100644 index 000000000..71f230a99 --- /dev/null +++ b/typescript/Frontend/src/dataCache/dataCache.ts @@ -0,0 +1,169 @@ +/* eslint-disable no-mixed-operators */ +import {TimeSpan, UnixTime} from "./time"; +import {Observable, Subject} from "rxjs"; +import {SkipList} from "./skipList/skipList"; +import {createDispatchQueue} from "./promiseQueue"; +import {SkipListNode} from "./skipList/skipListNode"; +import {RecordSeries} from "./data"; +import { Maybe, isUndefined } from "./utils/maybe"; + + +export const FetchResult = +{ + notAvailable : "N/A", + tryLater : "Try Later" +} as const + +export type FetchResult = + | T + | typeof FetchResult.notAvailable + | typeof FetchResult.tryLater + +function reverseBits(x : number): number +{ + // https://stackoverflow.com/a/60227327/141397 + + x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; + x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; + x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; + x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8; + x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16; + + return x >>> 0; +} + + +export default class DataCache> +{ + private readonly cache: SkipList> = new SkipList>() + private readonly resolution: TimeSpan; + + readonly _fetch: (t: UnixTime) => Promise>; + + private readonly fetchQueue = createDispatchQueue(6) + private readonly fetching: Set = new Set() + + public readonly gotData: Observable; + + constructor(fetch: (t: UnixTime) => Promise>, resolution: TimeSpan) + { + this._fetch = fetch; + this.resolution = resolution; + this.gotData = new Subject() + } + + public prefetch(times: Array, clear = true) + { + if (clear) + { + this.fetching.clear() + this.fetchQueue.clear() + } + + const timesWithPriority = times.map((time, index) => ({time, priority: reverseBits(index)})) + timesWithPriority.sort((x, y) => x.priority - y.priority) + + for (let i = 0; i < timesWithPriority.length; i++) + { + const time = timesWithPriority[i].time.round(this.resolution) + const t = time.ticks; + + const node = this.cache.find(t); + if (node.index !== t) + this.fetchData(time); + } + } + + public get(timeStamp: UnixTime, fetch = true): Maybe + { + const time = timeStamp.round(this.resolution) + const t = time.ticks; + + const node = this.cache.find(t); + if (node.index === t) + return node.value + + if (fetch) + this.fetchData(time); + + return this.interpolate(node, t) + } + + public getSeries(sampleTimes: UnixTime[]): RecordSeries + { + this.prefetch(sampleTimes) + return sampleTimes.map(time => ({time, value: this.get(time, false)})) + } + + private interpolate(before: SkipListNode>, t: number): Maybe + { + const dataBefore = before.value + const after = before.next[0]; + const dataAfter = after.value + + if (isUndefined(dataBefore) && isUndefined(dataAfter)) + return undefined + + if (isUndefined(dataBefore)) + return dataAfter + + if (isUndefined(dataAfter)) + return dataBefore + + const p = t - before.index + const n = after.index - t + const pn = p + n + + let interpolated: Partial> = {} + + //What about string nodes? like Alarms + for (const k of Object.keys(dataBefore)) + { + interpolated[k] = (dataBefore[k] * n + dataAfter[k] * p) / pn + } + + return interpolated as T + } + + private fetchData(time: UnixTime) + { + const t = time.ticks; + + if (this.fetching.has(t)) // we are already fetching t + return + + const fetchTask = () => + { + const onSuccess = (data: FetchResult) => + { + if (data === FetchResult.tryLater) + { + console.warn(FetchResult.tryLater) + return + } + + const value = data === FetchResult.notAvailable ? undefined : data; + this.cache.insert(value, t) + } + + const onFailure = (_: unknown) => + { + console.error(time.ticks + " FAILED!") // should not happen + } + + const dispatch = () => + { + this.fetching.delete(time.ticks); + (this.gotData as Subject).next(time); + } + + return this._fetch(time) + .then(d => onSuccess(d), f => onFailure(f)) + .finally(() => dispatch()) + }; + + this.fetching.add(t) + this.fetchQueue.dispatch(() => fetchTask()); + } + +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/linq.ts b/typescript/Frontend/src/dataCache/linq.ts new file mode 100644 index 000000000..1d566f445 --- /dev/null +++ b/typescript/Frontend/src/dataCache/linq.ts @@ -0,0 +1,20 @@ +// 0. Import Module +import { initializeLinq, IEnumerable } from "linq-to-typescript" +// 1. Declare that the JS types implement the IEnumerable interface +declare global { + interface Array extends IEnumerable { } + interface Uint8Array extends IEnumerable { } + interface Uint8ClampedArray extends IEnumerable { } + interface Uint16Array extends IEnumerable { } + interface Uint32Array extends IEnumerable { } + interface Int8Array extends IEnumerable { } + interface Int16Array extends IEnumerable { } + interface Int32Array extends IEnumerable { } + interface Float32Array extends IEnumerable { } + interface Float64Array extends IEnumerable { } + interface Map extends IEnumerable<[K, V]> { } + interface Set extends IEnumerable { } + interface String extends IEnumerable { } +} +// 2. Bind Linq Functions to Array, Map, etc +initializeLinq() \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/observableUtils.ts b/typescript/Frontend/src/dataCache/observableUtils.ts new file mode 100644 index 000000000..2869e2d60 --- /dev/null +++ b/typescript/Frontend/src/dataCache/observableUtils.ts @@ -0,0 +1,35 @@ +import {map, MonoTypeOperatorFunction, Observable, tap} from "rxjs"; +import {fastHash} from "./utils"; + +type ConcatX = [ + ...T[0], ...T[1], ...T[2], ...T[3], ...T[4], + ...T[5], ...T[6], ...T[7], ...T[8], ...T[9], + ...T[10], ...T[11], ...T[12], ...T[13], ...T[14], + ...T[15], ...T[16], ...T[17], ...T[18], ...T[19] +]; +type Flatten = + ConcatX<[...{ [K in keyof T]: T[K] extends any[] ? T[K] : [T[K]] }, ...[][]]> + + +export function flatten() +{ + return function>(source: Observable) + { + return source.pipe + ( + map(a => a.flat() as Flatten) + ) + } +} + +type RecursiveObject = T extends object ? T : never; + +type Terminals = +{ + [Key in keyof TModel]: TModel[Key] extends RecursiveObject + ? Terminals + : T; +}; + + + diff --git a/typescript/Frontend/src/dataCache/promiseQueue.ts b/typescript/Frontend/src/dataCache/promiseQueue.ts new file mode 100644 index 000000000..fd2b6bc0c --- /dev/null +++ b/typescript/Frontend/src/dataCache/promiseQueue.ts @@ -0,0 +1,51 @@ + + +export function createDispatchQueue(maxInflight: number, debug = false): { dispatch: (task: () => Promise) => number; clear: () => void } +{ + const queue: Array<() => Promise> = [] + + let inflight = 0; + + function done() + { + inflight-- + + if (debug && inflight + queue.length === 0) + console.log("queue empty") + + if (inflight < maxInflight && queue.length > 0) + { + const task = queue.pop()! + inflight++ + task().finally(() => done()) + } + } + + function dispatch(task: () => Promise) : number + { + if (inflight < maxInflight) + { + inflight++; + task().finally(() => done()) + } + else + { + if (debug && queue.length === 0) + console.log("queue in use") + + queue.push(task) + } + + return queue.length + } + + function clear() + { + // https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript + queue.length = 0 + if (debug) + console.log("queue cleared") + } + + return {dispatch, clear} +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/skipList/skipList.ts b/typescript/Frontend/src/dataCache/skipList/skipList.ts new file mode 100644 index 000000000..24187502d --- /dev/null +++ b/typescript/Frontend/src/dataCache/skipList/skipList.ts @@ -0,0 +1,80 @@ +import {find, findPath, insert, Path, SkipListNode} from "./skipListNode"; + +export class SkipList +{ + public readonly head: SkipListNode; + public readonly tail: SkipListNode; + + private readonly nLevels: number; + + private _length = 0 + + constructor(nLevels: number = 20) + { + // TODO: auto-levels + + this.tail = + { + index: Number.MAX_VALUE, + next: [], + value: undefined! + }; + + this.head = + { + index: Number.MIN_VALUE, + next: Array(nLevels).fill(this.tail), + value: undefined! + }; + + this.nLevels = nLevels + } + + public find(index: number, startNode = this.head, endNode = this.tail): SkipListNode + { + return find(index, startNode, endNode) + } + + private findPath(index: number, startNode = this.head, endNode = this.tail): Path + { + return findPath(index, startNode, endNode) + } + + public insert(value: T, index: number): SkipListNode + { + const path = this.findPath(index) + const node = path[0]; + + if (node.index === index) // overwrite + { + node.value = value + return node + } + + const nodeToInsert = {value, index, next: []} as SkipListNode + + const rnd = (Math.random() * (1 << this.nLevels)) << 0; + + for (let level = 0; level < this.nLevels; level++) + { + insert(nodeToInsert, path[level], level) + + if ((rnd & (1 << level)) === 0) + break + } + + this._length += 1 + + return nodeToInsert; + } + + get length(): number + { + return this._length; + } + + // public remove(index: number): void + // { + // // TODO + // } +} diff --git a/typescript/Frontend/src/dataCache/skipList/skipListNode.ts b/typescript/Frontend/src/dataCache/skipList/skipListNode.ts new file mode 100644 index 000000000..d523c6de2 --- /dev/null +++ b/typescript/Frontend/src/dataCache/skipList/skipListNode.ts @@ -0,0 +1,52 @@ +import {asMutableArray} from "../types"; + +export type Next = { readonly next: ReadonlyArray> } +export type Index = { readonly index: number } +export type Indexed = Index & { value: T } +export type SkipListNode = Next & Indexed +export type Path = SkipListNode[]; + +export function find(index: number, startNode: SkipListNode, endNode: SkipListNode): SkipListNode +{ + let node = startNode + + for (let level = startNode.next.length - 1; level >= 0; level--) + node = findOnLevel(index, node, endNode, level) + + return node +} + +export function findOnLevel(index: number, startNode: SkipListNode, endNode: SkipListNode, level: number): SkipListNode +{ + let node: SkipListNode = startNode + + while (true) + { + const next = node.next[level] + + if (index < next.index || endNode.index < next.index) + return node + + node = next + } +} + +export function findPath(index: number, startNode: SkipListNode, endNode: SkipListNode): Path +{ + const path = Array(startNode.next.length - 1) + let node = startNode + + for (let level = startNode.next.length - 1; level >= 0; level--) + { + node = findOnLevel(index, node, endNode, level) + path[level] = node + } + + return path +} + +export function insert(nodeToInsert: SkipListNode, after: SkipListNode, onLevel: number): void +{ + asMutableArray(nodeToInsert.next)[onLevel] = after.next[onLevel] + asMutableArray(after.next)[onLevel] = nodeToInsert +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/stringUtils.ts b/typescript/Frontend/src/dataCache/stringUtils.ts new file mode 100644 index 000000000..e9cbaca4a --- /dev/null +++ b/typescript/Frontend/src/dataCache/stringUtils.ts @@ -0,0 +1,5 @@ +export function trim(str: string, string: string = " "): string +{ + const pattern = '^[' + string + ']*(.*?)[' + string + ']*$'; + return str.replace(new RegExp(pattern), '$1') +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/time.ts b/typescript/Frontend/src/dataCache/time.ts new file mode 100644 index 000000000..43b8501c5 --- /dev/null +++ b/typescript/Frontend/src/dataCache/time.ts @@ -0,0 +1,302 @@ +import {trim} from "./stringUtils"; + +export class UnixTime +{ + private constructor(readonly ticks: number) + { + } + + public static readonly Epoch = new UnixTime(0) + + public static now(): UnixTime + { + return UnixTime.fromTicks(Date.now() / 1000) + } + + public static fromDate(date: Date): UnixTime + { + return UnixTime.fromTicks(date.getTime() / 1000) + } + + public toDate(): Date + { + return new Date(this.ticks * 1000) + } + + public static fromTicks(ticks: number): UnixTime + { + return new UnixTime(ticks) + } + + public later(timeSpan: TimeSpan): UnixTime + { + return new UnixTime(this.ticks + timeSpan.ticks) + } + + public move(ticks: number): UnixTime + { + return new UnixTime(this.ticks + ticks) + } + + public earlier(timeSpan: TimeSpan): UnixTime + { + return new UnixTime(this.ticks - timeSpan.ticks) + } + + public isEarlierThan(time: UnixTime): boolean + { + return this.ticks < time.ticks + } + + public isEarlierThanOrEqual(time: UnixTime): boolean + { + return this.ticks <= time.ticks + } + + public isLaterThan(time: UnixTime): boolean + { + return this.ticks > time.ticks + } + + public isLaterThanOrEqual(time: UnixTime): boolean + { + return this.ticks >= time.ticks + } + + public isEqual(time: UnixTime): boolean + { + return this.ticks === time.ticks + } + + public isInTheFuture(): boolean + { + return this.isLaterThan(UnixTime.now()) + } + + public isInThePast(): boolean + { + return this.ticks < UnixTime.now().ticks + } + + public round(ticks:number) : UnixTime + public round(duration: TimeSpan) : UnixTime + public round(durationOrTicks: TimeSpan | number) : UnixTime + { + const ticks = (typeof durationOrTicks === "number") ? durationOrTicks : durationOrTicks.ticks + + return new UnixTime(Math.round(this.ticks / ticks) * ticks) + } + + public rangeTo(time: UnixTime): TimeRange + { + return TimeRange.fromTimes(this, time); + } + + public rangeBefore(timeSpan: TimeSpan): TimeRange + { + return TimeRange.fromTimes(this.earlier(timeSpan), this); + } + + public rangeAfter(timeSpan: TimeSpan): TimeRange + { + return TimeRange.fromTimes(this, this.later(timeSpan)); + } + + public toString() : string + { + return this.ticks.toString() + } +} + + +export class TimeSpan +{ + private constructor(readonly ticks: number) {} + + get milliSeconds(): number { return this.ticks * 1000 } + get seconds() : number { return this.ticks } + get minutes() : number { return this.ticks / 60 } + get hours() : number { return this.minutes / 60 } + get days() : number { return this.hours / 24 } + get weeks() : number { return this.days / 7 } + + public static fromTicks (t: number): TimeSpan { return new TimeSpan(t) } + public static fromSeconds(t: number): TimeSpan { return TimeSpan.fromTicks(t) } + public static fromMinutes(t: number): TimeSpan { return TimeSpan.fromSeconds(t*60) } + public static fromHours (t: number): TimeSpan { return TimeSpan.fromMinutes(t*60) } + public static fromDays (t: number): TimeSpan { return TimeSpan.fromHours(t*24) } + public static fromWeeks (t: number): TimeSpan { return TimeSpan.fromDays(t*7) } + + public static span(from: UnixTime, to: UnixTime) : TimeSpan + { + return TimeSpan.fromTicks(Math.abs(to.ticks - from.ticks)) + } + + public add(timeSpan: TimeSpan) : TimeSpan + { + return TimeSpan.fromTicks(this.ticks + timeSpan.ticks) + } + + public subtract(timeSpan: TimeSpan) : TimeSpan + { + return TimeSpan.fromTicks(this.ticks - timeSpan.ticks) + } + + public divide(n: number) : TimeSpan + { + if (n <= 0) + throw 'n must be positive'; + + return TimeSpan.fromTicks(this.ticks/n) + } + + public multiply(n: number) : TimeSpan + { + if (n < 0) + throw 'n cannot be negative'; + + return TimeSpan.fromTicks(this.ticks * n) + } + + public round(ticks:number) : TimeSpan + public round(duration: TimeSpan) : TimeSpan + public round(durationOrTicks: TimeSpan | number) : TimeSpan + { + const ticks = (typeof durationOrTicks === "number") + ? durationOrTicks + : durationOrTicks.ticks + + return TimeSpan.fromTicks(Math.round(this.ticks / ticks) * ticks) + } + + + public toString() : string + { + let dt = 60*60*24*7 + + let ticks = this.ticks; + + if (ticks === 0) + return "0s" + + ticks = Math.abs(ticks) + + const nWeeks = Math.floor(ticks / dt) + ticks -= nWeeks * dt + + dt /= 7 + const nDays = Math.floor(ticks / dt) + ticks -= nDays * dt + + dt /= 24 + const nHours = Math.floor(ticks / dt) + ticks -= nHours * dt + + dt /= 60 + const nMinutes = Math.floor(ticks / dt) + ticks -= nMinutes * dt + + dt /= 60 + const nSeconds = Math.floor(ticks / dt) + + let s = "" + + if (nWeeks > 0) s += nWeeks .toString() + "w " + if (nDays > 0) s += nDays .toString() + "d " + if (nHours > 0) s += nHours .toString() + "h " + if (nMinutes > 0) s += nMinutes.toString() + "m " + if (nSeconds > 0) s += nSeconds.toString() + "s" + + return trim(s); + } +} + +export class TimeRange +{ + private constructor(private readonly from: number, private readonly to: number) + { + } + + public get start(): UnixTime + { + return UnixTime.fromTicks(this.from) + } + + public get mid(): UnixTime + { + return UnixTime.fromTicks((this.from + this.to) / 2) + } + + + public get end(): UnixTime + { + return UnixTime.fromTicks(this.to) + } + + public get duration(): TimeSpan + { + return TimeSpan.fromTicks(this.to - this.from) + } + + public static fromTimes(from: UnixTime, to: UnixTime): TimeRange + { + return from.isLaterThan(to) + ? new TimeRange(to.ticks, from.ticks) + : new TimeRange(from.ticks, to.ticks) + } + + public isInside(time: number) : boolean; + public isInside(time: UnixTime) : boolean; + public isInside(time: UnixTime | number) + { + const t = time instanceof UnixTime ? time.ticks : time + + return t >= this.from && t < this.to + } + + public sample(period: TimeSpan): UnixTime[] + { + const samples = [] + + for (let t = this.from; t < this.to; t += period.ticks) + samples.push(UnixTime.fromTicks(t)); + + return samples + } + + public subdivide(n: number) : TimeRange[] + { + if (n <= 0) + throw 'n must be positive'; + + const period = TimeSpan.fromTicks(this.duration.ticks / n); + if (period === this.duration) + return [this]; + + const samples = this.sample(period); + + const ranges : TimeRange[] = [] + + for (let i = 0; i < samples.length;) + ranges.push(TimeRange.fromTimes(samples[i], samples[++i])) + + return ranges + } + + + public earlier(dt: TimeSpan) : TimeRange + { + return new TimeRange(this.from - dt.ticks, this.to - dt.ticks) + } + + public later(dt: TimeSpan) : TimeRange + { + return new TimeRange(this.from + dt.ticks, this.to + dt.ticks) + } + + public move(ticks: number) : TimeRange + { + return new TimeRange(this.from + ticks, this.to + ticks) + } +} + diff --git a/typescript/Frontend/src/dataCache/types.ts b/typescript/Frontend/src/dataCache/types.ts new file mode 100644 index 000000000..665fd3663 --- /dev/null +++ b/typescript/Frontend/src/dataCache/types.ts @@ -0,0 +1,21 @@ +import {UnixTime} from "./time"; + +export type Timestamped = { time: UnixTime, value: T } + +export type Pair = [T1, T2] + +export type Position = { readonly x: number, readonly y: number } +export type Direction = { readonly dx: number, readonly dy: number } +export type Size = { readonly width: number, readonly height: number } +export type Rect = Position & Size + +export type Mutable = { -readonly [P in keyof T]: T[P] }; + +export type FieldKey = { [P in keyof T]: T[P] extends (...args: any) => any ? never : P }[keyof T]; +export type AllFields = Pick>; +export type SomeFields = Partial> +export const asMutable = (t: T) => (t as Mutable); +export const asMutableArray = (t: ReadonlyArray) => (t as Array); +export const cast = (t: unknown) => (t as T); + +export type Rename = Pick> & { [P in N]: T[K] } \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils.ts b/typescript/Frontend/src/dataCache/utils.ts new file mode 100644 index 000000000..bbf0350b1 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils.ts @@ -0,0 +1,119 @@ +import {IEnumerable} from "linq-to-typescript"; +import { isDefined } from "./utils/maybe"; + +//export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never + +export type Nothing = Record +// eslint-disable-next-line @typescript-eslint/no-empty-function + + + +export function fastHash(str: string): number +{ + const signed = str + .split('') + .reduce((p, c) => ((p << 5) - p) + c.charCodeAt(0) | 0, 0); + + return Math.abs(signed); +} + + + + + +// export function flattenObject(obj: object) : object +// { +// const flattened = {} +// +// for (const key of Object.keys(obj)) +// { +// // @ts-ignore +// const value = obj[key] +// +// if (typeof value === 'object' && value !== null && !Array.isArray(value)) +// { +// Object.assign(flattened, flattenObject(value)) +// } +// else +// { +// // @ts-ignore +// flattened[key] = value +// } +// } +// +// return flattened +// } +//return function>(source: Observable) + + + +export function* pairwise(iterable: Iterable, init?: T): Generator<[T, T]> +{ + const it = iterable[Symbol.iterator]() + + let first : T; + + if (isDefined(init)) + { + first = init + } + else + { + const f = it.next() + if (f.done) + return + + first = f.value + } + + let second = it.next() + + while(!second.done) + { + yield [first, second.value] + first = second.value + second = it.next() + } +} + +export function arraysEqual(a: Array, b: Array) +{ + if (a === b) return true; + if (a.length !== b.length) return false; + + for (let i = 0; i < a.length; ++i) + { + if (a[i] !== b[i]) return false; + } + + return true; +} + +export function mod(a:number, b:number) +{ + return ((a % b) + b) % b; +} + +export function clamp(a: number, min: number, max: number) +{ + return a > max ? max + : a < min ? min + : a +} + + +export function isDST(d : Date) +{ + const jan = new Date(d.getFullYear(), 0, 1).getTimezoneOffset(); + const jul = new Date(d.getFullYear(), 6, 1).getTimezoneOffset(); + + return Math.max(jan, jul) != d.getTimezoneOffset(); +} + +export function Transpose(src: IEnumerable>): IEnumerable> +{ + return src + .selectMany(line => line.select((element, column) => ({element, column}))) + .groupBy(i => i.column) + .select(g => g.select(e => e.element)); +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils/fileSystem.ts b/typescript/Frontend/src/dataCache/utils/fileSystem.ts new file mode 100644 index 000000000..f9975e8d8 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/fileSystem.ts @@ -0,0 +1,46 @@ +import fs from "fs"; + +export function doesFileExist(path: string): boolean +{ + try + { + fs.accessSync(path, fs.constants.F_OK); + return true; + } + catch (e) + { + return false; + } +} + +export function doesDirExist(path: string): boolean +{ + try + { + fs.readdirSync(path) + return true; + } + catch (e) + { + return false; + } +} + +export function readJsonFile(path: string) +{ + const data = fs.readFileSync(path, "utf-8") + return JSON.parse(data) as T +} + +export function writeJsonFile(path: string, contents: T) +{ + const data = JSON.stringify(contents) + return fs.writeFileSync(path, data, "utf-8") +} + +export function writeJsonFilePretty(path: string, contents: T) +{ + const data = JSON.stringify(contents,undefined,2) + return fs.writeFileSync(path, data, "utf-8") +} + diff --git a/typescript/Frontend/src/dataCache/utils/httpResponse.ts b/typescript/Frontend/src/dataCache/utils/httpResponse.ts new file mode 100644 index 000000000..c0fa1ee1e --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/httpResponse.ts @@ -0,0 +1,127 @@ + +import MimeType from "./mime"; +import fs from "fs"; +import http, {IncomingMessage, ServerResponse} from "http"; +import { Maybe } from "yup"; +import { getLogger } from "./logging"; +import { isDefined, isUndefined } from "./maybe"; +import { Dictionary } from "./utilityTypes"; +import { promisify } from "util"; +import { entries } from "./utils"; + + +const log = getLogger("HTTP") +const readFile = promisify(fs.readFile) + +export type HttpResponse = { + body: Maybe + headers : Dictionary + statusCode: number +} + + +function contentTypeHeader(mimeType: string) +{ + return {'Content-type': mimeType}; +} + +function forbidden(message = "403 : forbidden", headers: Dictionary = {}): HttpResponse +{ + return text(message, 403, headers) +} + +function notFound(message ="404 : not found", headers: Dictionary = {}): HttpResponse +{ + return text(message, 404, headers) +} + +function text(text: string, statusCode = 200, headers: Dictionary = {}): HttpResponse +{ + return { + statusCode: statusCode, + headers : {...headers, ...contentTypeHeader('text/plain')}, + body : text + }; +} + +function json(json: Dictionary, + replacer?: (k: string, v: unknown) => unknown, + headers: Dictionary = {}): HttpResponse +{ + return { + statusCode: 200, + headers : {...headers, 'Content-type': MimeType.json}, + body: JSON.stringify(json, replacer) + } +} + +function empty(headers: Dictionary = {}): HttpResponse +{ + return { + statusCode: 200, + headers, + body: undefined + } +} + +function ok(body: Maybe, headers: Dictionary = {}): HttpResponse +{ + return { + statusCode: 200, + headers, + body + } +} + +async function file(localRootPath: string, urlPath: string, headers: Dictionary = {}, defaultPath = "/"): Promise +{ + if (urlPath.includes('..')) + return HTTP.forbidden(); + + const localPath = localRootPath + (urlPath === "/" ? defaultPath : urlPath); + + const body = await readFile(localPath).catch(_ => undefined) + + if (isUndefined(body)) + return HTTP.notFound(); + + if (!('Content-type' in headers)) + { + headers = {...headers, ...contentTypeHeader(MimeType.guessFromPath(localPath))} + } + + return HTTP.ok(body, headers) +} + +function createServer(serve: (request: IncomingMessage) => Promise) +{ + async function wrapServe(request: IncomingMessage, response: ServerResponse): Promise + { + const r = await serve(request) + + entries(r.headers).forEach(([k, v]) => response.setHeader(k, v as any)) + + response.statusCode = r.statusCode + + if (isDefined(r.body)) + response.end(r.body) + else + response.end() + } + + return http.createServer(wrapServe); +} + +const HTTP = +{ + contentTypeHeader, + forbidden, + notFound, + ok, + json, + empty, + file, + createServer +} + +export default HTTP; diff --git a/typescript/Frontend/src/dataCache/utils/linq.ts b/typescript/Frontend/src/dataCache/utils/linq.ts new file mode 100644 index 000000000..e68de7db4 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/linq.ts @@ -0,0 +1,21 @@ +// 0. Import Module + +import {IEnumerable, initializeLinq} from "linq-to-typescript" +// 1. Declare that the JS types implement the IEnumerable interface +declare global { + interface Array extends IEnumerable { } + interface Uint8Array extends IEnumerable { } + interface Uint8ClampedArray extends IEnumerable { } + interface Uint16Array extends IEnumerable { } + interface Uint32Array extends IEnumerable { } + interface Int8Array extends IEnumerable { } + interface Int16Array extends IEnumerable { } + interface Int32Array extends IEnumerable { } + interface Float32Array extends IEnumerable { } + interface Float64Array extends IEnumerable { } + interface Map extends IEnumerable<[K, V]> { } + interface Set extends IEnumerable { } + interface String extends IEnumerable { } +} +// 2. Bind Linq Functions to Array, Map, etc +initializeLinq() \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils/logging.ts b/typescript/Frontend/src/dataCache/utils/logging.ts new file mode 100644 index 000000000..ec5bcd1f0 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/logging.ts @@ -0,0 +1,11 @@ +let subsystemPadding = 0 + +export function getLogger(subsystem: string): (msg: string) => void +{ + subsystemPadding = Math.max(subsystem.length, subsystemPadding) + + // eslint-disable-next-line no-console + return (msg: string) => console.log(`${new Date().toLocaleString()} | ${(subsystem.padEnd(subsystemPadding))} | ${msg}`); +} + + diff --git a/typescript/Frontend/src/dataCache/utils/match.ts b/typescript/Frontend/src/dataCache/utils/match.ts new file mode 100644 index 000000000..71106e986 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/match.ts @@ -0,0 +1,176 @@ +import {Dictionary, Func, Normalize1} from "./utilityTypes"; +import {isUndefined} from "./maybe"; +import {UnionToIntersection} from "simplytyped"; +import {current} from "immer"; +import { keys, valueToFunction } from "./utils"; + +// Type Compatibility +// https://www.typescriptlang.org/docs/handbook/type-compatibility.html + + +//TODO: review +export type IsUnionCase = + T extends Dictionary + ? [UnionToIntersection] extends [keyof T] + ? [keyof T] extends [UnionToIntersection] + ? true + : false + : false + : false + +//TODO: review +export type IsTaggedUnion = true extends UnionToIntersection> ? Dictionary : never + +export type Unwrap> = UnionToIntersection[keyof UnionToIntersection] ; + +export function update>(u: U, e: Partial>) +{ + const v = u as UnionToIntersection + const o = current(v) + + const ks = keys(v) + + if (ks.length != 1) + throw new Error("not a valid union case") + + const tag = ks[0] + + const before = v[tag]; + const before2 = current(before); + + + const newVar = {...before, ...e}; + v[tag] = newVar +} + +export function unwrap>(u: U) : Normalize1> +{ + const v = u as UnionToIntersection + + const ks = keys(v) + + if (ks.length != 1) + throw new Error("not a valid union case") + + const key = ks[0] + return v[key] as any; +} + +export function base>(u: U): Normalize1 & Partial>>> +{ + return unwrap(u) as Normalize1 & Partial>>> +} + +export function tag>(u: U) : keyof UnionToIntersection +{ + const v = u as UnionToIntersection + + const ks = keys(v) + + if (ks.length != 1) + throw new Error("not a valid union case") + + return ks[0] +} + +export function tagsEqual>(u: U, v: U) : v is U +{ + return tag(u) === tag(v) +} + + +type MapFuncs = { [k in keyof UnionToIntersection]: Func[k]> } +type OtherwiseKeys = Exclude, keyof M>; + +type OtherwiseArg = { + [k in keyof UnionToIntersection]: Record[k]> +}[OtherwiseKeys] + +type OtherwiseFunc>, R> = Func extends never ? unknown : OtherwiseArg, R>; + + +export function match, M extends Partial>, R>(uCase: U, matchFuncs: M, otherwise: OtherwiseFunc | R):{ [k in keyof M]: M[k] extends Func ? O : never }[keyof M] | R +{ + const otw = valueToFunction(otherwise) + + const c = uCase as UnionToIntersection + + const ks = keys(c) + + if (ks.length != 1) + return otw(c) + + const key = ks[0] + const arg = c[key] + + const matchFunc = matchFuncs[key] + + if (isUndefined(matchFunc)) + return otw(c); + + return matchFunc(arg as any) as any; +} + + +export function dispatch>() +{ + // type Intersection = UnionToIntersection; + // + // type MapFuncs = { [k in keyof Intersection]: Func } + // type OtherwiseKeys = Exclude; + // + // type OtherwiseArg = { + // [k in keyof Intersection]: Record + // }[OtherwiseKeys] + // + // type OtherwiseFunc, R> = Func extends never ? unknown : OtherwiseArg, R>; + + return >, R>(matchFuncs: M, otherwise: OtherwiseFunc | R) => + { + const otw = valueToFunction(otherwise) + + return (uCase: U): { [k in keyof M]: M[k] extends Func ? O : never }[keyof M] | R => + { + const c = uCase as UnionToIntersection + + const ks = keys(c) + + if (ks.length != 1) + return otw(c) + + const key = ks[0] + const arg = c[key] + + const matchFunc = matchFuncs[key] + + if (isUndefined(matchFunc)) + return otw(c); + + return matchFunc(arg as any) as any; + } + }; +} + + +export function concat, T extends Dictionary>(rec: R, t:T) +{ + + const result = {} as { + [k in keyof UnionToIntersection]: Record[k] & T> + }[keyof UnionToIntersection] + + for (const k in rec) + { + + // @ts-ignore + result[k] = { ...rec[k], ...t} + } + + return result +} + + + + + + diff --git a/typescript/Frontend/src/dataCache/utils/maybe.ts b/typescript/Frontend/src/dataCache/utils/maybe.ts new file mode 100644 index 000000000..7ab47fdb9 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/maybe.ts @@ -0,0 +1,16 @@ +export type Maybe = T | undefined | null; + +export function isDefined(e: Maybe): e is T +{ + return e != undefined // != by design to include null +} + +export function isUndefined(e: Maybe): e is undefined | null +{ + return e == undefined // == by design to include null +} + +export function toArray(e: Maybe): T[] +{ + return isDefined(e) ? [e] : [] +} diff --git a/typescript/Frontend/src/dataCache/utils/milliseconds.ts b/typescript/Frontend/src/dataCache/utils/milliseconds.ts new file mode 100644 index 000000000..7d0daf94a --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/milliseconds.ts @@ -0,0 +1,18 @@ + +export type Milliseconds = number + +export const Milliseconds = +{ + fromSeconds: (count: number): Milliseconds => count * 1000, + fromMinutes: (count: number): Milliseconds => count * 1000 * 60, + fromHours : (count: number): Milliseconds => count * 1000 * 60 * 60, + fromDays : (count: number): Milliseconds => count * 1000 * 60 * 60 * 24, + fromWeeks : (count: number): Milliseconds => count * 1000 * 60 * 60 * 24 * 7, + + toSeconds: (count: Milliseconds): number => count / 1000, + toMinutes: (count: Milliseconds): number => count / 1000 / 60, + toHours : (count: Milliseconds): number => count / 1000 / 60 / 60, + toDays : (count: Milliseconds): number => count / 1000 / 60 / 60 / 24, + toWeeks : (count: Milliseconds): number => count / 1000 / 60 / 60 / 24 / 7, +} as const + diff --git a/typescript/Frontend/src/dataCache/utils/mime.ts b/typescript/Frontend/src/dataCache/utils/mime.ts new file mode 100644 index 000000000..87dc60150 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/mime.ts @@ -0,0 +1,32 @@ +import PlatformPath from "path"; +import { isDefined } from "./maybe"; + + +function guessFromPath(path: string) : string +{ + const ext = PlatformPath.parse(path).ext?.substring(1) as keyof typeof MimeType; + + const mimeType = MimeType[ext] + + return isDefined(mimeType) && typeof mimeType === "string" + ? mimeType + : 'application/octet-stream' +} + +const MimeType = +{ + ico : 'image/x-icon', + html: 'text/html; charset=UTF-8', + js : 'text/javascript', + json: 'application/json; charset=UTF-8', + css : 'text/css; charset=UTF-8', + png : 'image/png', + jpg : 'image/jpeg', + wav : 'audio/wav', + mp3 : 'audio/mpeg', + svg : 'image/svg+xml; charset=UTF-8', + pdf : 'application/pdf', + guessFromPath +}; + +export default MimeType \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils/path.ts b/typescript/Frontend/src/dataCache/utils/path.ts new file mode 100644 index 000000000..6d82809b2 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/path.ts @@ -0,0 +1,81 @@ +import {isUndefined} from "./maybe"; +import {from, IEnumerable} from "linq-to-typescript"; +import {isBoolean, isNumber, isPlainObject, isString} from "./runtimeTypeChecking"; + +function getAt(root: any, path: (keyof any)[]) +{ + return path.reduce((v, p) => v[p], root) +} + + +function iterate(root: unknown): IEnumerable<{ path: string[]; node: unknown }> +{ + if (isUndefined(root)) + return [] + + return from(iterate(root)) + + function* iterate(node: unknown, path: string[] = []): Generator<{ path: string[]; node: unknown }> + { + if (isString(node) || isNumber(node) || isBoolean(node)) + yield {path, node} + else if (isPlainObject(node)) + for (const key in node) + { + path.push(key) + yield {path, node} + yield* iterate(node[key], path) + path.pop() + } + } +} + +function iterateLeafs(root: unknown): IEnumerable<{ path: string[]; node: unknown }> +{ + if (isUndefined(root)) + return [] + + return from(iterate(root)) + + function* iterate(node: unknown, path: string[] = []): Generator<{ path: string[]; node: unknown }> + { + if (isString(node) || isNumber(node) || isBoolean(node)) + yield {path, node} + else if (isPlainObject(node)) + for (const key in node) + { + path.push(key) + yield* iterate(node[key], path) + path.pop() + } + } +} + + +function iterateBranches(root: unknown): IEnumerable<{ path: string[]; node: unknown }> +{ + if (isUndefined(root)) + return [] + + return from(iterate(root)) + + function* iterate(node: unknown, path: string[] = []): Generator<{ path: string[]; node: unknown }> + { + if (isPlainObject(node)) + for (const key in node) + { + path.push(key) + yield {path, node} + yield* iterate(node[key], path) + path.pop() + } + } +} + +export const Path = +{ + iterate, + iterateLeafs, + iterateBranches, + getAt +} as const diff --git a/typescript/Frontend/src/dataCache/utils/requestUtils.ts b/typescript/Frontend/src/dataCache/utils/requestUtils.ts new file mode 100644 index 000000000..8757cb3cc --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/requestUtils.ts @@ -0,0 +1,45 @@ +import {IncomingMessage} from "http"; +import {firstValueFrom, map, Observable, startWith, toArray} from "rxjs"; + +export function observeData(request: IncomingMessage, maxLength: number = Number.POSITIVE_INFINITY): Observable +{ + let nBytes = 0; + + return new Observable(subscriber => + { + request.on('end', () => subscriber.complete()); + request.on('data', (data: Uint8Array) => + { + nBytes += data.byteLength + + if (nBytes <= maxLength) + subscriber.next(data); + else + { + const error = `too much data: expected ${maxLength} bytes or less, got ${nBytes} bytes.`; + subscriber.error(error); + request.destroy(new Error(error)) + } + }); + }); +} + +export async function getRequestJson(request: IncomingMessage, maxLength = 500000): Promise +{ + const data = await getData(request, maxLength) + return JSON.parse(data.toString()) +} + +const noData = new Uint8Array(0); + +export function getData(request: IncomingMessage, maxLength: number = Number.POSITIVE_INFINITY): Promise +{ + const data = observeData(request, maxLength).pipe + ( + startWith(noData), + toArray(), + map(b => Buffer.concat(b)), // cannot inline! + ) + + return firstValueFrom(data); +} \ No newline at end of file diff --git a/typescript/Frontend/src/dataCache/utils/runtimeTypeChecking.ts b/typescript/Frontend/src/dataCache/utils/runtimeTypeChecking.ts new file mode 100644 index 000000000..c24de1606 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/runtimeTypeChecking.ts @@ -0,0 +1,66 @@ +export type TypeCode = + | "undefined" + | "object" + | "boolean" + | "number" + | "string" + | "function" + | "symbol" + | "bigint"; + +export type PlainObject = Record + +export function isObject(thing: unknown) : thing is object +{ + return typeof thing === "object" +} + +export function isDate(thing: unknown) : thing is Date +{ + return thing instanceof Date +} + +export function isPlainObject(thing: unknown) : thing is PlainObject +{ + return isObject(thing) && !isDate(thing) +} + +export function isArray(thing: unknown) : thing is Array +{ + return Array.isArray(thing) +} + +export function isNumber(thing: unknown) : thing is number +{ + return typeof thing === "number" +} + +export function isBoolean(thing: unknown) : thing is boolean +{ + return typeof thing === "boolean" +} + +export function isString(thing: unknown) : thing is string +{ + return typeof thing === "string" +} + +// export function isFunction(thing: unknown): thing is (...args: unknown[]) => unknown +// { +// return typeof thing === "function" +// } + +export function isFunction(obj: unknown): obj is (...args: any[]) => any +{ + return obj instanceof Function; +} + +export function isSymbol(thing: unknown) : thing is symbol +{ + return typeof thing === "symbol" +} + +export function isBigint(thing: unknown) : thing is bigint +{ + return typeof thing === "bigint" +} diff --git a/typescript/Frontend/src/dataCache/utils/stringUtils.ts b/typescript/Frontend/src/dataCache/utils/stringUtils.ts new file mode 100644 index 000000000..b377c01fe --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/stringUtils.ts @@ -0,0 +1,22 @@ +export function toLowercaseAscii(string: string) +{ + return string + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .toLowerCase(); +} + +export function containsIgnoringAccents(string: string, substring: string) +{ + if (substring === "") return true; + if (string === "") return false; + + substring = "" + substring; + + if (substring.length > string.length) + return false; + + return toLowercaseAscii(string).includes(toLowercaseAscii(substring)); +} + + diff --git a/typescript/Frontend/src/dataCache/utils/tree.ts b/typescript/Frontend/src/dataCache/utils/tree.ts new file mode 100644 index 000000000..88a83f785 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/tree.ts @@ -0,0 +1,71 @@ +import {from, IEnumerable} from "linq-to-typescript"; +import {isDefined, isUndefined, Maybe} from "./maybe"; + + +export function Tree(getChildren: (t: T) => IEnumerable) +{ + function iterate(root: Maybe): IEnumerable + { + if (isUndefined(root)) + return [] + + return from(iterateTree()) + + function* iterateTree() + { + const queue: T[] = [root!] + + do + { + const element = queue.shift()! + yield element + for (const child of getChildren(element)) + queue.push(child) + } + while (queue.length > 0) + } + } + + + function iterateWithPath(root: Maybe): IEnumerable + { + return isDefined(root) + ? from(iterateTreeWithPath()) + : []; + + function* iterateTreeWithPath() + { + const stack: Array> = [[root!]] + + while (true) + { + const head = stack[0]; + + if (head.length > 0) + { + yield stack + .select(l => l[0]) + .toArray() + + const children = getChildren(head[0]).toArray() + stack.unshift(children) + } + else + { + stack.shift() // remove empty array in front + if(stack.length > 0) + stack[0].shift() + else + break; + } + } + + } + } + + return { + iterate, + iterateWithPath + } as const +} + diff --git a/typescript/Frontend/src/dataCache/utils/type.ts b/typescript/Frontend/src/dataCache/utils/type.ts new file mode 100644 index 000000000..da775fb19 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/type.ts @@ -0,0 +1,115 @@ +export {} +// export type Type = +// | "number" +// | "object" +// | "string" +// | "never" +// | "any" +// | "unknown" +// | "undefined" +// | "boolean" +// | "bigint" +// | "symbol" +// | Property[] +// | Func +// +// export type Key = "string" | "number" | "symbol" +// +// export type Property = Func | +// { +// key: Key, +// type: Type, +// readonly? : boolean, +// nullable? : boolean +// } +// +// export type Arg = +// { +// name: string, +// type: Type, +// nullable? : boolean +// } +// +// export type Func = +// { +// args: Arg[], +// returnType: Type, +// } +// +// +// type X = Partial +// +// export function render(t: Type, indent = 0) +// { +// if (typeof t === "string") +// return t +// +// +// return "ERROR" +// } +// + + +type DeviceType = + | "Pv" + | "Load" + | "Battery" + | "Grid" + | "Inverter" + | "AcInToAcOut" + | "DcDc" + | "AcInBus" + | "AcOutBus" + | "DcBus" + | "Dc48Bus" // low voltage DC Bus, to be eliminated in later versions + + +type Phase = +{ + voltage : number // U, non-negative + current : number // I, sign depends on device type, see sign convention below +} + +type AcPhase = Phase & +{ + phi : number // [0,2pi) +} + +type Device = +{ + Type: DeviceType, + Name?: string, +} + +type Stack = +{ + Top? : Device[], // 0 to N + Right? : Device // 0 or 1 + Bottom? : Device[] // 0 to N + Disconnected?: boolean // not present = false +} + +/// A DC device must have a field denoting its DC connection +type DcDevice = Device & +{ + Dc : Phase +} + + +/// An AC device can have 1 to 3 AC phases +/// An AC device also needs a Frequency measurement +/// Total power can be obtained by summing the power of the phases +type AcDevice = Device & +{ + Ac: AcPhase[] + Frequency: number +} + + +/// A low voltage 48V DC device +/// Needed to distinguish the two sides of the DCDC +/// Will be dropped once we get HV batteries +type Dc48Device = Device & +{ + dc48 : Phase +} diff --git a/typescript/Frontend/src/dataCache/utils/utilityTypes.ts b/typescript/Frontend/src/dataCache/utils/utilityTypes.ts new file mode 100644 index 000000000..66f57ee96 --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/utilityTypes.ts @@ -0,0 +1,52 @@ +import {UnionToIntersection} from "simplytyped"; + +export type Dictionary = Record +export type Nothing = Dictionary + +export type IsUnion = [T] extends [UnionToIntersection] ? false : true; + +export type UnionToDeepPartialIntersection = DeepPartial> +export type UnionToPartialIntersection = Partial> + +export type Func = (arg: T) => R +export type AsyncFunc = (arg: T) => Promise + +export type SyncAction = (arg: T) => void +export type AsyncAction = (arg: T) => Promise +export type Action = SyncAction | AsyncAction + +export type Lazy = () => T +export type Base64 = string +export type ValueOf = T[keyof T]; + +export type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; } : T; +export type DeepMutable = { -readonly [P in keyof T]: DeepMutable }; +export type Mutable = { -readonly [P in keyof T]: T[P] }; +export type NumberLiteralToStringLiteral = T extends number ? `${T}` : T + +export type KeyedChildren = { children?: Dictionary } + +export type Union = { [S in K] : V} + +export type IntersectionToUnion = { [Prop in keyof T]: Record }[keyof T] // not sure if this is aptly named + +// helper to flatten (instantiate) types in editor popups +// eslint-disable-next-line @typescript-eslint/ban-types + +export type Normalize = T extends (...args: infer A) => infer R ? (...args: Normalize) => Normalize + : [T] extends [any] ? { [K in keyof T]: Normalize } + : never + +export type Normalize1 = T extends (...args: infer A) => infer R ? (...args: A) => R + : [T] extends [any] ? { [K in keyof T]: T[K] } + : T + +export type Normalize2 = T extends (...args: infer A) => infer R ? (...args: Normalize1) => Normalize1 + : [T] extends [any] ? { [K in keyof T]: Normalize1 } + : never + +export function mutable(t: T) +{ + return t as Mutable +} + diff --git a/typescript/Frontend/src/dataCache/utils/utils.ts b/typescript/Frontend/src/dataCache/utils/utils.ts new file mode 100644 index 000000000..8e922df8a --- /dev/null +++ b/typescript/Frontend/src/dataCache/utils/utils.ts @@ -0,0 +1,55 @@ + +import {IncomingMessage} from "http"; +import {from} from "linq-to-typescript"; +import { Maybe, isUndefined } from "./maybe"; +import { Dictionary, Func } from "./utilityTypes"; + +type StringValued = +{ + [Key in keyof T]: T[Key] extends number ? Maybe + : T[Key] extends string ? Maybe + : T[Key] extends boolean ? Maybe + : never +} + +export function getQueryParams(request: IncomingMessage): Maybe> +{ + if (isUndefined(request.url)) + return undefined + + const url = new URL(request.url, `https://${request.headers.host}/`); + + const query: Dictionary = {} + const urlSearchParams = new URLSearchParams(url.search); + + if (!from(urlSearchParams.entries()).any()) + return undefined + + for (const [key, value] of urlSearchParams.entries()) + query[key] = value; + + return query as StringValued; +} + +export function getPath(req: IncomingMessage) +{ + return new URL(req.url!, `https://${req.headers.host}/`).pathname; +} + +export function entries(t: T) +{ + return Object.entries(t as Dictionary) +} + +export function keys(t: T): (keyof T)[] +{ + return Object.keys(t as object) as (keyof T)[] +} + +export function valueToFunction(tr: Func | R) : Func +{ + if (typeof tr === "function") + return tr as Func + + return (_: T) => tr +} \ No newline at end of file diff --git a/typescript/Frontend/src/hooks/useToken.tsx b/typescript/Frontend/src/hooks/useToken.tsx index e20c7198b..b7e7ed706 100644 --- a/typescript/Frontend/src/hooks/useToken.tsx +++ b/typescript/Frontend/src/hooks/useToken.tsx @@ -2,18 +2,18 @@ import { useState } from "react"; const useToken = () => { const getToken = () => { - const tokenString = sessionStorage.getItem("token"); + const tokenString = localStorage.getItem("token"); return tokenString !== null ? JSON.parse(tokenString) : ""; }; const [token, setToken] = useState(getToken()); const saveToken = (userToken: any) => { - sessionStorage.setItem("token", JSON.stringify(userToken)); + localStorage.setItem("token", JSON.stringify(userToken)); setToken(userToken); }; const removeToken = () => { - sessionStorage.removeItem("token"); + localStorage.removeItem("token"); setToken(null); }; diff --git a/typescript/Frontend/src/index.tsx b/typescript/Frontend/src/index.tsx index a5c14d630..262364be0 100644 --- a/typescript/Frontend/src/index.tsx +++ b/typescript/Frontend/src/index.tsx @@ -15,6 +15,15 @@ const theme = createTheme({ primary: { main: "#F59100", }, + text: { + primary: "#000000", + secondary: "#000000", + disabled: "#000000", + }, + }, + typography: { + fontFamily: `"Ubuntu", sans-serif`, + fontWeightRegular: 300, }, }); diff --git a/typescript/Frontend/src/resources/innovenergy_Logo_onOrange.png b/typescript/Frontend/src/resources/innovenergy_Logo_onOrange.png new file mode 100644 index 000000000..22e9c6412 Binary files /dev/null and b/typescript/Frontend/src/resources/innovenergy_Logo_onOrange.png differ diff --git a/typescript/Frontend/src/util/graph.util.tsx b/typescript/Frontend/src/util/graph.util.tsx new file mode 100644 index 000000000..8bbf435c8 --- /dev/null +++ b/typescript/Frontend/src/util/graph.util.tsx @@ -0,0 +1,68 @@ +import { Datum, TypedArray } from "plotly.js"; +import { RecordSeries } from "../dataCache/data"; +import { isDefined } from "../dataCache/utils/maybe"; + +export const mergeDeep = (...objects: any[]) => { + const isObject = (obj: GraphCoordinates) => obj && typeof obj === "object"; + return objects.reduce((prev, obj) => { + Object.keys(obj).forEach((key) => { + const pVal = prev[key]; + const oVal = obj[key]; + + if (Array.isArray(pVal) && Array.isArray(oVal)) { + prev[key] = pVal.concat(...oVal); + } else if (isObject(pVal) && isObject(oVal)) { + prev[key] = mergeDeep(pVal, oVal); + } else { + prev[key] = oVal; + } + }); + + return prev; + }, {} as GraphData); +}; + +export const transformToGraphData = (timeStampData: RecordSeries) => { + return timeStampData.reduce((acc, curr) => { + if (isDefined(curr.value)) { + const timeStampObj = Object.keys(curr.value).reduce( + (pathAcc, currPath) => { + if (currPath) { + return { + ...pathAcc, + [currPath]: { + x: [curr.time.ticks], + y: [curr.value ? curr.value[currPath] : 0], + }, + }; + } + return pathAcc; + }, + {} as GraphData + ); + return mergeDeep(acc, timeStampObj); + } + return acc; + }, {} as GraphData); +}; + +export interface GraphCoordinates { + x: Datum[] | Datum[][] | TypedArray; + y: Datum[] | Datum[][] | TypedArray; +} + +export interface GraphData { + [path: string]: GraphCoordinates; +} + +export const parseCsv = (text: string) => { + const y = text + .split(/\r?\n/) + .map((l) => l.split(";")) + .filter((fields) => !isNaN(parseFloat(fields[1]))); + + const x = y + .map((fields) => ({ [fields[0]]: parseFloat(fields[1]) })) + .reduce((acc, current) => ({ ...acc, ...current }), {}); + return x; +}; diff --git a/typescript/Frontend/src/util/installation.util.tsx b/typescript/Frontend/src/util/installation.util.tsx index e1719c224..648393443 100644 --- a/typescript/Frontend/src/util/installation.util.tsx +++ b/typescript/Frontend/src/util/installation.util.tsx @@ -3,9 +3,9 @@ import { styled, Tab, Tabs } from "@mui/material"; export const StyledTab = styled((props: any) => ( ))(({ theme }) => ({ - textTransform: "none", + textTransform: "uppercase", fontWeight: theme.typography.fontWeightRegular, - fontSize: theme.typography.pxToRem(15), + fontSize: theme.typography.pxToRem(14), marginRight: theme.spacing(1), background: "0 0", border: "1px solid transparent", @@ -15,9 +15,9 @@ export const StyledTab = styled((props: any) => ( textDecoration: "none", transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`, "&.Mui-selected": { - color: "#495057", + color: "#000000", backgroundColor: "#fff", - borderColor: "#dee2e6 #dee2e6 #fff", + borderColor: "#bdbdbd #bdbdbd #fff", marginBottom: "-3px", }, "&.Mui-focusVisible": { @@ -26,19 +26,22 @@ export const StyledTab = styled((props: any) => ( })); export const AntTabs = styled(Tabs)({ - borderBottom: "1px solid #dee2e6", + borderBottom: "1px solid #bdbdbd", overflow: "visible!important", "& div.MuiTabs-scroller": { overflow: "visible!important", }, "&.Mui-selected": { - color: "#495057", + color: "#000000", backgroundColor: "red", - borderColor: `#dee2e6 #dee2e6 #fff`, + borderColor: `#bdbdbd #bdbdbd #fff`, }, "& .MuiTabs-indicator": { display: "flex", justifyContent: "center", backgroundColor: "transparent", }, + "&.MuiTabs-root": { + width: "100%", + }, }); diff --git a/typescript/Frontend/tsconfig.json b/typescript/Frontend/tsconfig.json index f199ca8fd..423cb75a1 100644 --- a/typescript/Frontend/tsconfig.json +++ b/typescript/Frontend/tsconfig.json @@ -7,6 +7,7 @@ "esnext" ], "allowJs": true, + "downlevelIteration": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true,