diff --git a/Dockerfile b/Dockerfile
index 59677c69b7ffce60fcccbea4501d5a1e92c4cc0b..19606527adbac48def6672dd4f1518ac96526048 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # build stage
-FROM gitlab.ub.uni-bielefeld.de/cmg/clowm/dependency_proxy/containers/node:18 as build-stage
+FROM node:18 as build-stage
 WORKDIR /app
 COPY package.json ./
 COPY package-lock.json ./
@@ -8,10 +8,11 @@ COPY . .
 RUN npm run build-only
 
 # production stage
-FROM gitlab.ub.uni-bielefeld.de/cmg/clowm/dependency_proxy/containers/nginx:stable-alpine as production-stage
+FROM nginx:stable-alpine as production-stage
+EXPOSE 80
 HEALTHCHECK --interval=30s --timeout=4s CMD curl --head -f http://localhost || exit 1
 COPY --from=build-stage /app/dist /usr/share/nginx/html
 COPY --from=build-stage /app/src/assets/env.template.js /tmp
 COPY nginx.conf /etc/nginx/conf.d/default.conf
-EXPOSE 80
+
 CMD ["/bin/sh",  "-c",  "envsubst < /tmp/env.template.js > /usr/share/nginx/html/env.js && exec nginx -g 'daemon off;'"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
index 4733d826e19f1495e86207bb7bd713ed97185739..66b63f03ea3738f5ca76a22b139670937c781c34 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,11 @@ When accessing the website, these variables will be loaded dynamically into the
 |-------------------------|---------|----------|--------------------------------------------------|
 | `AUTH_API_BASE_URL`     | unset   | HTTP URL | Base URL for the Auth Service API                |
 | `WORKFLOW_API_BASE_URL` | unset   | HTTP URL | Base URL for the Workflow Service API            |
+| `RESOURCE_API_BASE_URL` | unset   | HTTP URL | Base URL for the Resource Service API            |
 | `S3PROXY_API_BASE_URL`  | unset   | HTTP URL | Base URL for the S3Proxy Service API             |
 | `S3_URL`                | unset   | HTTP URL | URL of the S3 storage to interact with           |
 | `DEV_SYSTEM`            | `false` | boolean  | Flag if the service is installed on a Dev system |
+
+## License
+
+The API is licensed under the [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) license. See the [License](LICENSE) file for more information
diff --git a/index.html b/index.html
index 61088270988b1c5c2bd89e8457bf83bc02b0d28b..9723102fbf222a41d7425880bad64e75952b7018 100644
--- a/index.html
+++ b/index.html
@@ -1,14 +1,15 @@
 <!DOCTYPE html>
 <html lang="en" data-bs-theme="light">
-  <head>
-    <meta charset="UTF-8" />
-    <link rel="icon" href="/favicon.ico" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>CloWM</title>
+<head>
+    <meta charset="UTF-8"/>
+    <link rel="icon" href="/favicon.ico"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+    <title>CloWM - Cloud-based Workflow Manager</title>
+    <meta name="description" content="The Cloud-based Workflow Manager (CloWM) is a service hosted at Bielefeld University that can transform your Nextflow workflow into a public webservice and provide compute and storage resources for executing your registered workflow"/>
     <script src="/env.js"></script>
-  </head>
-  <body>
-    <div id="app" class="d-flex flex-column justify-content-between" style="min-height: 100vh"></div>
-    <script type="module" src="/src/main.ts"></script>
-  </body>
+</head>
+<body>
+<div id="app" class="d-flex flex-column justify-content-between" style="min-height: 100vh"></div>
+<script type="module" src="/src/main.ts"></script>
+</body>
 </html>
diff --git a/package-lock.json b/package-lock.json
index 526ec7500ee19be500f24e93b3b264b18cc526e8..b30b02ddd8e043765e363950f1e7880fc4c9d451 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,7 @@
     "": {
       "name": "clowm-ui",
       "version": "2.0.0",
+      "license": "Apache-2.0",
       "dependencies": {
         "@aws-sdk/client-s3": "^3.440.0",
         "@aws-sdk/lib-storage": "^3.440.0",
@@ -14,45 +15,46 @@
         "@fortawesome/fontawesome-free": "~6.4.2",
         "@popperjs/core": "~2.11.8",
         "ajv": "~8.12.0",
-        "bootstrap": "~5.3.1",
-        "chart.js": "~4.3.3",
+        "bootstrap": "~5.3.0",
+        "chart.js": "~4.4.0",
         "chartjs-plugin-zoom": "~2.0.1",
-        "dayjs": "~1.11.9",
+        "dayjs": "~1.11.0",
         "dompurify": "~3.0.5",
         "filesize": "~10.0.12",
         "idb-keyval": "^6.2.1",
-        "pinia": "~2.1.6",
-        "semver": "~7.5.4",
+        "pinia": "~2.1.0",
+        "semver": "~7.5.0",
         "showdown": "~2.1.0",
-        "vue": "~3.3.4",
-        "vue-router": "~4.2.4",
-        "vue3-cookies": "~1.0.6"
+        "vue": "~3.4.0",
+        "vue-router": "~4.2.0",
+        "vue3-cookies": "~1.0.0"
       },
       "devDependencies": {
         "@esbuild-plugins/node-globals-polyfill": "~0.2.3",
         "@esbuild-plugins/node-modules-polyfill": "~0.2.2",
         "@rushstack/eslint-patch": "~1.2.0",
         "@tsconfig/node18": "^18.2.1",
-        "@types/bootstrap": "~5.2.6",
-        "@types/dompurify": "~3.0.2",
-        "@types/node": "^16.18.48",
+        "@types/bootstrap": "~5.2.0",
+        "@types/dompurify": "~3.0.0",
+        "@types/node": "^18.19.5",
         "@types/semver": "~7.5.1",
         "@types/showdown": "~2.0.1",
-        "@vitejs/plugin-vue": "~4.3.4",
+        "@vitejs/plugin-vue": "~5.0.0",
         "@vue/eslint-config-prettier": "~8.0.0",
         "@vue/eslint-config-typescript": "~11.0.3",
-        "@vue/tsconfig": "~0.4.0",
+        "@vue/tsconfig": "~0.5.0",
         "axios": "~1.6.0",
         "eslint": "~8.48.0",
-        "eslint-plugin-vue": "~9.17.0",
+        "eslint-plugin-vue": "~9.19.0",
+        "highlight.js": "^11.9.0",
         "npm-run-all": "~4.1.5",
-        "openapi-typescript-codegen": "^0.25.0",
+        "openapi-typescript-codegen": "^0.26.0",
         "prettier": "~3.0.3",
         "rollup-plugin-node-polyfills": "~0.2.1",
         "sass": "~1.66.1",
         "typescript": "~5.1.6",
-        "vite": "~4.4.9",
-        "vue-tsc": "~1.8.10"
+        "vite": "~5.0.0",
+        "vue-tsc": "~1.8.0"
       }
     },
     "node_modules/@aashutoshrathi/word-wrap": {
@@ -65,15 +67,22 @@
       }
     },
     "node_modules/@apidevtools/json-schema-ref-parser": {
-      "version": "9.0.9",
-      "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz",
-      "integrity": "sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==",
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.1.0.tgz",
+      "integrity": "sha512-g/VW9ZQEFJAOwAyUb8JFf7MLiLy2uEB4rU270rGzDwICxnxMlPy0O11KVePSgS36K1NI29gSlK84n5INGhd4Ag==",
       "dev": true,
       "dependencies": {
         "@jsdevtools/ono": "^7.1.3",
-        "@types/json-schema": "^7.0.6",
-        "call-me-maybe": "^1.0.1",
-        "js-yaml": "^4.1.0"
+        "@types/json-schema": "^7.0.13",
+        "@types/lodash.clonedeep": "^4.5.7",
+        "js-yaml": "^4.1.0",
+        "lodash.clonedeep": "^4.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/philsturgeon"
       }
     },
     "node_modules/@aws-crypto/crc32": {
@@ -202,65 +211,66 @@
       "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
     },
     "node_modules/@aws-sdk/client-s3": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.468.0.tgz",
-      "integrity": "sha512-j0MnSYKu7KRAPXXn5egmJBzzPAgM/Hb0UUr0CHRrj8eMV7Ni/cZQpbU8tqgFel7BrsS4YINB5W/Q3FShHpI/8w==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.495.0.tgz",
+      "integrity": "sha512-Lr08ygmesFScyF7TK70uS4O9YLTaKHH4O/FGNKw17DpI5XyyS/Aje9yVqn6Yi3OUrsKChxGK1n0gc6ipyUGsjQ==",
       "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.468.0",
-        "@aws-sdk/core": "3.468.0",
-        "@aws-sdk/credential-provider-node": "3.468.0",
-        "@aws-sdk/middleware-bucket-endpoint": "3.468.0",
-        "@aws-sdk/middleware-expect-continue": "3.468.0",
-        "@aws-sdk/middleware-flexible-checksums": "3.468.0",
-        "@aws-sdk/middleware-host-header": "3.468.0",
-        "@aws-sdk/middleware-location-constraint": "3.468.0",
-        "@aws-sdk/middleware-logger": "3.468.0",
-        "@aws-sdk/middleware-recursion-detection": "3.468.0",
-        "@aws-sdk/middleware-sdk-s3": "3.468.0",
-        "@aws-sdk/middleware-signing": "3.468.0",
-        "@aws-sdk/middleware-ssec": "3.468.0",
-        "@aws-sdk/middleware-user-agent": "3.468.0",
-        "@aws-sdk/region-config-resolver": "3.468.0",
-        "@aws-sdk/signature-v4-multi-region": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@aws-sdk/util-endpoints": "3.468.0",
-        "@aws-sdk/util-user-agent-browser": "3.468.0",
-        "@aws-sdk/util-user-agent-node": "3.468.0",
-        "@aws-sdk/xml-builder": "3.465.0",
-        "@smithy/config-resolver": "^2.0.20",
-        "@smithy/eventstream-serde-browser": "^2.0.15",
-        "@smithy/eventstream-serde-config-resolver": "^2.0.15",
-        "@smithy/eventstream-serde-node": "^2.0.15",
-        "@smithy/fetch-http-handler": "^2.3.1",
-        "@smithy/hash-blob-browser": "^2.0.16",
-        "@smithy/hash-node": "^2.0.17",
-        "@smithy/hash-stream-node": "^2.0.17",
-        "@smithy/invalid-dependency": "^2.0.15",
-        "@smithy/md5-js": "^2.0.17",
-        "@smithy/middleware-content-length": "^2.0.17",
-        "@smithy/middleware-endpoint": "^2.2.2",
-        "@smithy/middleware-retry": "^2.0.23",
-        "@smithy/middleware-serde": "^2.0.15",
-        "@smithy/middleware-stack": "^2.0.9",
-        "@smithy/node-config-provider": "^2.1.7",
-        "@smithy/node-http-handler": "^2.2.1",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
-        "@smithy/url-parser": "^2.0.15",
-        "@smithy/util-base64": "^2.0.1",
-        "@smithy/util-body-length-browser": "^2.0.1",
-        "@smithy/util-body-length-node": "^2.1.0",
-        "@smithy/util-defaults-mode-browser": "^2.0.22",
-        "@smithy/util-defaults-mode-node": "^2.0.28",
-        "@smithy/util-endpoints": "^1.0.6",
-        "@smithy/util-retry": "^2.0.8",
-        "@smithy/util-stream": "^2.0.23",
-        "@smithy/util-utf8": "^2.0.2",
-        "@smithy/util-waiter": "^2.0.15",
+        "@aws-sdk/client-sts": "3.495.0",
+        "@aws-sdk/core": "3.495.0",
+        "@aws-sdk/credential-provider-node": "3.495.0",
+        "@aws-sdk/middleware-bucket-endpoint": "3.495.0",
+        "@aws-sdk/middleware-expect-continue": "3.495.0",
+        "@aws-sdk/middleware-flexible-checksums": "3.495.0",
+        "@aws-sdk/middleware-host-header": "3.495.0",
+        "@aws-sdk/middleware-location-constraint": "3.495.0",
+        "@aws-sdk/middleware-logger": "3.495.0",
+        "@aws-sdk/middleware-recursion-detection": "3.495.0",
+        "@aws-sdk/middleware-sdk-s3": "3.495.0",
+        "@aws-sdk/middleware-signing": "3.495.0",
+        "@aws-sdk/middleware-ssec": "3.495.0",
+        "@aws-sdk/middleware-user-agent": "3.495.0",
+        "@aws-sdk/region-config-resolver": "3.495.0",
+        "@aws-sdk/signature-v4-multi-region": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@aws-sdk/util-endpoints": "3.495.0",
+        "@aws-sdk/util-user-agent-browser": "3.495.0",
+        "@aws-sdk/util-user-agent-node": "3.495.0",
+        "@aws-sdk/xml-builder": "3.495.0",
+        "@smithy/config-resolver": "^2.1.0",
+        "@smithy/core": "^1.3.0",
+        "@smithy/eventstream-serde-browser": "^2.1.0",
+        "@smithy/eventstream-serde-config-resolver": "^2.1.0",
+        "@smithy/eventstream-serde-node": "^2.1.0",
+        "@smithy/fetch-http-handler": "^2.4.0",
+        "@smithy/hash-blob-browser": "^2.1.0",
+        "@smithy/hash-node": "^2.1.0",
+        "@smithy/hash-stream-node": "^2.1.0",
+        "@smithy/invalid-dependency": "^2.1.0",
+        "@smithy/md5-js": "^2.1.0",
+        "@smithy/middleware-content-length": "^2.1.0",
+        "@smithy/middleware-endpoint": "^2.4.0",
+        "@smithy/middleware-retry": "^2.1.0",
+        "@smithy/middleware-serde": "^2.1.0",
+        "@smithy/middleware-stack": "^2.1.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/node-http-handler": "^2.3.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/url-parser": "^2.1.0",
+        "@smithy/util-base64": "^2.1.0",
+        "@smithy/util-body-length-browser": "^2.1.0",
+        "@smithy/util-body-length-node": "^2.2.0",
+        "@smithy/util-defaults-mode-browser": "^2.1.0",
+        "@smithy/util-defaults-mode-node": "^2.1.0",
+        "@smithy/util-endpoints": "^1.1.0",
+        "@smithy/util-retry": "^2.1.0",
+        "@smithy/util-stream": "^2.1.0",
+        "@smithy/util-utf8": "^2.1.0",
+        "@smithy/util-waiter": "^2.1.0",
         "fast-xml-parser": "4.2.5",
         "tslib": "^2.5.0"
       },
@@ -269,45 +279,46 @@
       }
     },
     "node_modules/@aws-sdk/client-sso": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.468.0.tgz",
-      "integrity": "sha512-NabkDaiFsMP8lBR3+JzdtOVarH8kCJst30fQyBIs2PI0uMfajFJ+SK9JTg1J1YZY6aNJBxo2Bxu3dl0fjZ5N/g==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.495.0.tgz",
+      "integrity": "sha512-Uerh3aDe/JeQNjcyXKI+8VuKPOAB6mCUKlScD0AIca1Kdyk8PsQTq4rDzFCYAQsNS5/BuPN+Ak0NqwsJM0agYA==",
       "dependencies": {
         "@aws-crypto/sha256-browser": "3.0.0",
         "@aws-crypto/sha256-js": "3.0.0",
-        "@aws-sdk/core": "3.468.0",
-        "@aws-sdk/middleware-host-header": "3.468.0",
-        "@aws-sdk/middleware-logger": "3.468.0",
-        "@aws-sdk/middleware-recursion-detection": "3.468.0",
-        "@aws-sdk/middleware-user-agent": "3.468.0",
-        "@aws-sdk/region-config-resolver": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@aws-sdk/util-endpoints": "3.468.0",
-        "@aws-sdk/util-user-agent-browser": "3.468.0",
-        "@aws-sdk/util-user-agent-node": "3.468.0",
-        "@smithy/config-resolver": "^2.0.20",
-        "@smithy/fetch-http-handler": "^2.3.1",
-        "@smithy/hash-node": "^2.0.17",
-        "@smithy/invalid-dependency": "^2.0.15",
-        "@smithy/middleware-content-length": "^2.0.17",
-        "@smithy/middleware-endpoint": "^2.2.2",
-        "@smithy/middleware-retry": "^2.0.23",
-        "@smithy/middleware-serde": "^2.0.15",
-        "@smithy/middleware-stack": "^2.0.9",
-        "@smithy/node-config-provider": "^2.1.7",
-        "@smithy/node-http-handler": "^2.2.1",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
-        "@smithy/url-parser": "^2.0.15",
-        "@smithy/util-base64": "^2.0.1",
-        "@smithy/util-body-length-browser": "^2.0.1",
-        "@smithy/util-body-length-node": "^2.1.0",
-        "@smithy/util-defaults-mode-browser": "^2.0.22",
-        "@smithy/util-defaults-mode-node": "^2.0.28",
-        "@smithy/util-endpoints": "^1.0.6",
-        "@smithy/util-retry": "^2.0.8",
-        "@smithy/util-utf8": "^2.0.2",
+        "@aws-sdk/core": "3.495.0",
+        "@aws-sdk/middleware-host-header": "3.495.0",
+        "@aws-sdk/middleware-logger": "3.495.0",
+        "@aws-sdk/middleware-recursion-detection": "3.495.0",
+        "@aws-sdk/middleware-user-agent": "3.495.0",
+        "@aws-sdk/region-config-resolver": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@aws-sdk/util-endpoints": "3.495.0",
+        "@aws-sdk/util-user-agent-browser": "3.495.0",
+        "@aws-sdk/util-user-agent-node": "3.495.0",
+        "@smithy/config-resolver": "^2.1.0",
+        "@smithy/core": "^1.3.0",
+        "@smithy/fetch-http-handler": "^2.4.0",
+        "@smithy/hash-node": "^2.1.0",
+        "@smithy/invalid-dependency": "^2.1.0",
+        "@smithy/middleware-content-length": "^2.1.0",
+        "@smithy/middleware-endpoint": "^2.4.0",
+        "@smithy/middleware-retry": "^2.1.0",
+        "@smithy/middleware-serde": "^2.1.0",
+        "@smithy/middleware-stack": "^2.1.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/node-http-handler": "^2.3.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/url-parser": "^2.1.0",
+        "@smithy/util-base64": "^2.1.0",
+        "@smithy/util-body-length-browser": "^2.1.0",
+        "@smithy/util-body-length-node": "^2.2.0",
+        "@smithy/util-defaults-mode-browser": "^2.1.0",
+        "@smithy/util-defaults-mode-node": "^2.1.0",
+        "@smithy/util-endpoints": "^1.1.0",
+        "@smithy/util-retry": "^2.1.0",
+        "@smithy/util-utf8": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -315,48 +326,48 @@
       }
     },
     "node_modules/@aws-sdk/client-sts": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.468.0.tgz",
-      "integrity": "sha512-EausH7ezv1AIgl/4rfZRNRxrFND5hChbIqkuAf8e5wZ74HUEVBMmD5Jiwfs0WRCso3ejOjsNtS8PAOA3djn28w==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.495.0.tgz",
+      "integrity": "sha512-lXQIx7D1MQ5+F8PaSYV7UiSxgP9M5ba/YFx1rcxi5l1GlbAWuHWhrk15qKe9d6vLxa2eTjJFiVzbO7pJqRBEWw==",
       "dependencies": {
         "@aws-crypto/sha256-browser": "3.0.0",
         "@aws-crypto/sha256-js": "3.0.0",
-        "@aws-sdk/core": "3.468.0",
-        "@aws-sdk/credential-provider-node": "3.468.0",
-        "@aws-sdk/middleware-host-header": "3.468.0",
-        "@aws-sdk/middleware-logger": "3.468.0",
-        "@aws-sdk/middleware-recursion-detection": "3.468.0",
-        "@aws-sdk/middleware-sdk-sts": "3.468.0",
-        "@aws-sdk/middleware-signing": "3.468.0",
-        "@aws-sdk/middleware-user-agent": "3.468.0",
-        "@aws-sdk/region-config-resolver": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@aws-sdk/util-endpoints": "3.468.0",
-        "@aws-sdk/util-user-agent-browser": "3.468.0",
-        "@aws-sdk/util-user-agent-node": "3.468.0",
-        "@smithy/config-resolver": "^2.0.20",
-        "@smithy/fetch-http-handler": "^2.3.1",
-        "@smithy/hash-node": "^2.0.17",
-        "@smithy/invalid-dependency": "^2.0.15",
-        "@smithy/middleware-content-length": "^2.0.17",
-        "@smithy/middleware-endpoint": "^2.2.2",
-        "@smithy/middleware-retry": "^2.0.23",
-        "@smithy/middleware-serde": "^2.0.15",
-        "@smithy/middleware-stack": "^2.0.9",
-        "@smithy/node-config-provider": "^2.1.7",
-        "@smithy/node-http-handler": "^2.2.1",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
-        "@smithy/url-parser": "^2.0.15",
-        "@smithy/util-base64": "^2.0.1",
-        "@smithy/util-body-length-browser": "^2.0.1",
-        "@smithy/util-body-length-node": "^2.1.0",
-        "@smithy/util-defaults-mode-browser": "^2.0.22",
-        "@smithy/util-defaults-mode-node": "^2.0.28",
-        "@smithy/util-endpoints": "^1.0.6",
-        "@smithy/util-retry": "^2.0.8",
-        "@smithy/util-utf8": "^2.0.2",
+        "@aws-sdk/core": "3.495.0",
+        "@aws-sdk/credential-provider-node": "3.495.0",
+        "@aws-sdk/middleware-host-header": "3.495.0",
+        "@aws-sdk/middleware-logger": "3.495.0",
+        "@aws-sdk/middleware-recursion-detection": "3.495.0",
+        "@aws-sdk/middleware-user-agent": "3.495.0",
+        "@aws-sdk/region-config-resolver": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@aws-sdk/util-endpoints": "3.495.0",
+        "@aws-sdk/util-user-agent-browser": "3.495.0",
+        "@aws-sdk/util-user-agent-node": "3.495.0",
+        "@smithy/config-resolver": "^2.1.0",
+        "@smithy/core": "^1.3.0",
+        "@smithy/fetch-http-handler": "^2.4.0",
+        "@smithy/hash-node": "^2.1.0",
+        "@smithy/invalid-dependency": "^2.1.0",
+        "@smithy/middleware-content-length": "^2.1.0",
+        "@smithy/middleware-endpoint": "^2.4.0",
+        "@smithy/middleware-retry": "^2.1.0",
+        "@smithy/middleware-serde": "^2.1.0",
+        "@smithy/middleware-stack": "^2.1.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/node-http-handler": "^2.3.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/url-parser": "^2.1.0",
+        "@smithy/util-base64": "^2.1.0",
+        "@smithy/util-body-length-browser": "^2.1.0",
+        "@smithy/util-body-length-node": "^2.2.0",
+        "@smithy/util-defaults-mode-browser": "^2.1.0",
+        "@smithy/util-defaults-mode-node": "^2.1.0",
+        "@smithy/util-endpoints": "^1.1.0",
+        "@smithy/util-middleware": "^2.1.0",
+        "@smithy/util-retry": "^2.1.0",
+        "@smithy/util-utf8": "^2.1.0",
         "fast-xml-parser": "4.2.5",
         "tslib": "^2.5.0"
       },
@@ -365,11 +376,15 @@
       }
     },
     "node_modules/@aws-sdk/core": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.468.0.tgz",
-      "integrity": "sha512-ezUJR9VvknKoXzNZ4wvzGi1jdkmm+/1dUYQ9Sw4r8bzlJDTsUnWbyvaDlBQh81RuhLtVkaUfTnQKoec0cwlZKQ==",
-      "dependencies": {
-        "@smithy/smithy-client": "^2.1.18",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.495.0.tgz",
+      "integrity": "sha512-TI/jq1cSUR+r1prJ9xXtxMO0u2/jXrWjf3Z2ekForsCObPtR9qkJCYyezargupoSJqZA60KUpOhxrKW/dFJ1rw==",
+      "dependencies": {
+        "@smithy/core": "^1.3.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/signature-v4": "^2.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -377,13 +392,13 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-env": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.468.0.tgz",
-      "integrity": "sha512-k/1WHd3KZn0EQYjadooj53FC0z24/e4dUZhbSKTULgmxyO62pwh9v3Brvw4WRa/8o2wTffU/jo54tf4vGuP/ZA==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.495.0.tgz",
+      "integrity": "sha512-2CKlHiQRXyVA7t3VGXo39a/UwRrZs/VG0jYZFu60dK9afxesRkA4XOJto765VenT/eR3LkeVW+RBzOISHUFg0Q==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/property-provider": "^2.0.0",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -391,19 +406,19 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-ini": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.468.0.tgz",
-      "integrity": "sha512-DBYsptYBq0xC+GTh+3dN3Q9/wRZiPpsHA4yCC1mskEbJfMy7EIZZKtZ8lOkZ24NOI5oea4o3L+wFTxOeFSKntA==",
-      "dependencies": {
-        "@aws-sdk/credential-provider-env": "3.468.0",
-        "@aws-sdk/credential-provider-process": "3.468.0",
-        "@aws-sdk/credential-provider-sso": "3.468.0",
-        "@aws-sdk/credential-provider-web-identity": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/credential-provider-imds": "^2.0.0",
-        "@smithy/property-provider": "^2.0.0",
-        "@smithy/shared-ini-file-loader": "^2.0.6",
-        "@smithy/types": "^2.7.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.495.0.tgz",
+      "integrity": "sha512-DGRfND+FIacuQQNozMa8fS4yUrWZgkB6CEH4ghiqUvtE7h2sGMMVEerlaCGgTnQlpWWvDS656orzwEO3vuMTVw==",
+      "dependencies": {
+        "@aws-sdk/credential-provider-env": "3.495.0",
+        "@aws-sdk/credential-provider-process": "3.495.0",
+        "@aws-sdk/credential-provider-sso": "3.495.0",
+        "@aws-sdk/credential-provider-web-identity": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/credential-provider-imds": "^2.2.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/shared-ini-file-loader": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -411,20 +426,20 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-node": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.468.0.tgz",
-      "integrity": "sha512-iZlWWZXp6zAH4sP3VrqF7RpAmzl8Qr8tuVkF7ubUZhzyWzKfhLVzqRJqbMYCBPGmfZLAZWjsziPHaBErYkG/5g==",
-      "dependencies": {
-        "@aws-sdk/credential-provider-env": "3.468.0",
-        "@aws-sdk/credential-provider-ini": "3.468.0",
-        "@aws-sdk/credential-provider-process": "3.468.0",
-        "@aws-sdk/credential-provider-sso": "3.468.0",
-        "@aws-sdk/credential-provider-web-identity": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/credential-provider-imds": "^2.0.0",
-        "@smithy/property-provider": "^2.0.0",
-        "@smithy/shared-ini-file-loader": "^2.0.6",
-        "@smithy/types": "^2.7.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.495.0.tgz",
+      "integrity": "sha512-OH3lV7erPLNxkZQ+QBEgX353mseelBaHutyJNFKdgCYMZUhENu2DNTvkasGtwA24TqG0sRiuO2yNhpqP8IF+LA==",
+      "dependencies": {
+        "@aws-sdk/credential-provider-env": "3.495.0",
+        "@aws-sdk/credential-provider-ini": "3.495.0",
+        "@aws-sdk/credential-provider-process": "3.495.0",
+        "@aws-sdk/credential-provider-sso": "3.495.0",
+        "@aws-sdk/credential-provider-web-identity": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/credential-provider-imds": "^2.2.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/shared-ini-file-loader": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -432,14 +447,14 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-process": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.468.0.tgz",
-      "integrity": "sha512-OYSn1A/UsyPJ7Z8Q2cNhTf55O36shPmSsvOfND04nSfu1nPaR+VUvvsP7v+brhGpwC/GAKTIdGAo4blH31BS6A==",
-      "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/property-provider": "^2.0.0",
-        "@smithy/shared-ini-file-loader": "^2.0.6",
-        "@smithy/types": "^2.7.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.495.0.tgz",
+      "integrity": "sha512-AouHJtg5qXeqzlY5plqbBkQPea1Kd3/tz9wfN+d5gbTUsDBlV7R6IinzhJWWgniS0jsaEOronlCXLIEOWUzTsw==",
+      "dependencies": {
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/shared-ini-file-loader": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -447,16 +462,16 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-sso": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.468.0.tgz",
-      "integrity": "sha512-eIdGoIw10xyBm7TDcV5Y/W7tzNs2f4H+2G5ZdjG2XGLAELsKCoixe+9ZB662MLtLCxvm7eE1GjOjKsSua6MImQ==",
-      "dependencies": {
-        "@aws-sdk/client-sso": "3.468.0",
-        "@aws-sdk/token-providers": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/property-provider": "^2.0.0",
-        "@smithy/shared-ini-file-loader": "^2.0.6",
-        "@smithy/types": "^2.7.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.495.0.tgz",
+      "integrity": "sha512-brbgLtws+jmBPm6FrQ0CT2mHCgFKdopwxJj/4+j//OH0aAgzBH5gOztoDu1R556KU9K8Co220J79gJWV3s40zQ==",
+      "dependencies": {
+        "@aws-sdk/client-sso": "3.495.0",
+        "@aws-sdk/token-providers": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/shared-ini-file-loader": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -464,13 +479,13 @@
       }
     },
     "node_modules/@aws-sdk/credential-provider-web-identity": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.468.0.tgz",
-      "integrity": "sha512-rexymPmXjtkwCPfhnUq3EjO1rSkf39R4Jz9CqiM7OsqK2qlT5Y/V3gnMKn0ZMXsYaQOMfM3cT5xly5R+OKDHlw==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.495.0.tgz",
+      "integrity": "sha512-w4S01mdQZ8kQn4J6CM2Fgral9xtNBh8h5i4DWSOwFxfiokott59zDoFMWJRUdUHzXsnAGULC8+wJ4VeiZZBq1Q==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/property-provider": "^2.0.0",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -478,13 +493,13 @@
       }
     },
     "node_modules/@aws-sdk/lib-storage": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.468.0.tgz",
-      "integrity": "sha512-0z2dtg0YQOik6kNM7Xyqgwjk1n5P4hdNVD3MGcjYElPBnp5jVIIZSv7KGaMzgtKSCaHtkjqMidS1DS8Oae9YJg==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.495.0.tgz",
+      "integrity": "sha512-/Vypp54qRUeXMeGVS6DPplK1kK/uzDIH6wl9hR9XxZCXHzKaDoC8O9uLE+9c4LLJy+/mC9KjpQUq9WMs2QRo5g==",
       "dependencies": {
-        "@smithy/abort-controller": "^2.0.1",
-        "@smithy/middleware-endpoint": "^2.2.2",
-        "@smithy/smithy-client": "^2.1.18",
+        "@smithy/abort-controller": "^2.1.0",
+        "@smithy/middleware-endpoint": "^2.4.0",
+        "@smithy/smithy-client": "^2.3.0",
         "buffer": "5.6.0",
         "events": "3.3.0",
         "stream-browserify": "3.0.0",
@@ -498,16 +513,16 @@
       }
     },
     "node_modules/@aws-sdk/middleware-bucket-endpoint": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.468.0.tgz",
-      "integrity": "sha512-Dak7sSaPxkTWuBzvFI0zXL1t+/6JdeZZVLRckp2reoQ46CY/hnCbd5/wITtO7CYyjHX7WrEILjTynfZoa1E7Qw==",
-      "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@aws-sdk/util-arn-parser": "3.465.0",
-        "@smithy/node-config-provider": "^2.1.7",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-config-provider": "^2.0.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.495.0.tgz",
+      "integrity": "sha512-KJ9hvVFsDIavaUWwT+nDcbyHNNYx0GQ9W0HQ136VR0Uuy3srHrIU2QivmhUi2n8SaDptm4t2K4osSKqgfxH3cQ==",
+      "dependencies": {
+        "@aws-sdk/types": "3.495.0",
+        "@aws-sdk/util-arn-parser": "3.495.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-config-provider": "^2.2.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -515,13 +530,13 @@
       }
     },
     "node_modules/@aws-sdk/middleware-expect-continue": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.468.0.tgz",
-      "integrity": "sha512-/wmLjmfgeulxhhmnxX3X3N933TvGsYckVIFjAtDSpLjqkbwzEcNiLq7AdmNJ4BfxG0MCMgcht561DCCD19x8Bg==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.495.0.tgz",
+      "integrity": "sha512-WbSZ2AquYb6Wfdr3CZRO37dOSFhE2pnR7GuST+kWEYL+sLnNN3Ms85Bf2YUHNqnTNwD/R7KWw6I5CkyDRxLnkw==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -529,17 +544,17 @@
       }
     },
     "node_modules/@aws-sdk/middleware-flexible-checksums": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.468.0.tgz",
-      "integrity": "sha512-LQwL/N5MCj3Y5keLLewHTqeAXUIMsHFZyxDXRm/uxrOon9ufLKDvGvzAmfwn1/CuSUo66ZfT8VPSA4BsC90RtA==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.495.0.tgz",
+      "integrity": "sha512-9pgwS4oLi0DhVo8V2dk6JB5HH5FOyLmnwBABWXDy3ASR6L7Rs+y/y3+jiS/wl7nUNOEFXbBfvdaQ1bI1t6+MDA==",
       "dependencies": {
         "@aws-crypto/crc32": "3.0.0",
         "@aws-crypto/crc32c": "3.0.0",
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/is-array-buffer": "^2.0.0",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-utf8": "^2.0.2",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/is-array-buffer": "^2.1.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-utf8": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -547,13 +562,13 @@
       }
     },
     "node_modules/@aws-sdk/middleware-host-header": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.468.0.tgz",
-      "integrity": "sha512-gwQ+/QhX+lhof304r6zbZ/V5l5cjhGRxLL3CjH1uJPMcOAbw9wUlMdl+ibr8UwBZ5elfKFGiB1cdW/0uMchw0w==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.495.0.tgz",
+      "integrity": "sha512-qqE6mVxbyJwn59NQMvtYyaZT3GEZnmsvBUry3sDtU7Be1g9w5OKhY4CnAAQyXZI288iQUtyxxDh+hnSLy6RFjA==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -561,12 +576,12 @@
       }
     },
     "node_modules/@aws-sdk/middleware-location-constraint": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.468.0.tgz",
-      "integrity": "sha512-0gBX/lDynQr4YIhM9h1dVnkVWqrg+34iOCVIUq8jHxzUzgZWglGkG9lHGGg0r1xkLTmegeoo1OKH8wrQ6n33Cg==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.495.0.tgz",
+      "integrity": "sha512-XFgoK+Pr4olMo6VqUnffwi7XvnoJv7Lm8qlZ5LiijcQGl7ZJZ9FOwYzrbGX8CuTXwfydOKrxyPNywUsS5LDeDw==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -574,12 +589,12 @@
       }
     },
     "node_modules/@aws-sdk/middleware-logger": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.468.0.tgz",
-      "integrity": "sha512-X5XHKV7DHRXI3f29SAhJPe/OxWRFgDWDMMCALfzhmJfCi6Jfh0M14cJKoC+nl+dk9lB+36+jKjhjETZaL2bPlA==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.495.0.tgz",
+      "integrity": "sha512-sgmr9fpCSg3rFvMnvfKeN7dhY+AmUpZPPWyc+s1kgQONeLUUxQkbdqR2/V+tz2ZPxUBD2dToTG/JhtMcIKmt4Q==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -587,13 +602,13 @@
       }
     },
     "node_modules/@aws-sdk/middleware-recursion-detection": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.468.0.tgz",
-      "integrity": "sha512-vch9IQib2Ng9ucSyRW2eKNQXHUPb5jUPCLA5otTW/8nGjcOU37LxQG4WrxO7uaJ9Oe8hjHO+hViE3P0KISUhtA==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.495.0.tgz",
+      "integrity": "sha512-jhuOcLsMrHengJy/oz6Waumwx/vtSMKnEbROR7qZ7CaTDHRUbriPYXGen7CHCs/6aWN0UeI3JBAqwlnSW5tpIg==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -601,32 +616,18 @@
       }
     },
     "node_modules/@aws-sdk/middleware-sdk-s3": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.468.0.tgz",
-      "integrity": "sha512-8Ma8tdHYH0stMmGQHh/8eI53oAfiuUJvnQdILWcNArAwlVXt+DJirCSGWP8SqvYdKGa4+jr1YW3+nTdhnm2FZg==",
-      "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@aws-sdk/util-arn-parser": "3.465.0",
-        "@smithy/node-config-provider": "^2.1.7",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/signature-v4": "^2.0.0",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-config-provider": "^2.0.0",
-        "tslib": "^2.5.0"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      }
-    },
-    "node_modules/@aws-sdk/middleware-sdk-sts": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.468.0.tgz",
-      "integrity": "sha512-xRy8NKfHbmafHwdbotdWgHBvRs0YZgk20GrhFJKp43bkqVbJ5bNlh3nQXf1DeFY9fARR84Bfotya4fwCUHWgZg==",
-      "dependencies": {
-        "@aws-sdk/middleware-signing": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/types": "^2.7.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.495.0.tgz",
+      "integrity": "sha512-H6DPpRKJZaaElLo60nyZNGcZHrCVMq8tErEQPD/g5v4ZrAjLJlxJ1N/hVhw5IP3Q6ZXVNB2PhNi6pp9Lzd1kqQ==",
+      "dependencies": {
+        "@aws-sdk/types": "3.495.0",
+        "@aws-sdk/util-arn-parser": "3.495.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/signature-v4": "^2.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-config-provider": "^2.2.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -634,16 +635,16 @@
       }
     },
     "node_modules/@aws-sdk/middleware-signing": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.468.0.tgz",
-      "integrity": "sha512-s+7fSB1gdnnTj5O0aCCarX3z5Vppop8kazbNSZADdkfHIDWCN80IH4ZNjY3OWqaAz0HmR4LNNrovdR304ojb4Q==",
-      "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/property-provider": "^2.0.0",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/signature-v4": "^2.0.0",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-middleware": "^2.0.8",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.495.0.tgz",
+      "integrity": "sha512-QZuWRo6JQ7UKeHzqqnP/qmUXirVKXSMXSEFtpOHio/JkQPASVlD1TNs5L6RL7dKrnqLrg/jpTiw4b0UdAU8kOw==",
+      "dependencies": {
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/signature-v4": "^2.1.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-middleware": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -651,12 +652,12 @@
       }
     },
     "node_modules/@aws-sdk/middleware-ssec": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.468.0.tgz",
-      "integrity": "sha512-y1qLW24wRkOGBTK5d6eJXf6d8HYo4rzT4a1mNDN1rd18NSffwQ6Yke5qeUiIaxa0y/l+FvvNYErbhYtij2rJoQ==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.495.0.tgz",
+      "integrity": "sha512-obXxHpCY8NPFgCkiqANLgTa0T1bAlEJCgXVsmCWasKLW1rrMrtVuJBNyOtk0NPx2XCJodsKJc+/9Mz8ByEOd5A==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -664,14 +665,14 @@
       }
     },
     "node_modules/@aws-sdk/middleware-user-agent": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.468.0.tgz",
-      "integrity": "sha512-lmqaEChVWK6MvNpM/LH504pRsP3p/IuZugWwxCbelKw4bGVU4IgG3mbjfATiIlHo4rW8ttHh1bTsZIGjWOqNeA==",
-      "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@aws-sdk/util-endpoints": "3.468.0",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/types": "^2.7.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.495.0.tgz",
+      "integrity": "sha512-n+lC43Z7+LyAF7b63bR+e5pBmBqPaqh4gupEmrORc4wKsX7U4OncDPiVn5jPD7ZC3IZbLeTuDsjQOK8Ev+Hraw==",
+      "dependencies": {
+        "@aws-sdk/types": "3.495.0",
+        "@aws-sdk/util-endpoints": "3.495.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -679,14 +680,15 @@
       }
     },
     "node_modules/@aws-sdk/region-config-resolver": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.468.0.tgz",
-      "integrity": "sha512-EkDfaumuBhDJFg4lmvWiBE8Ln4BF6hYNC2YfkjKCTEuePy5BKryFedwylYZZ3CJG/uVyfr8xBy+mvoR8plpHjg==",
-      "dependencies": {
-        "@smithy/node-config-provider": "^2.1.7",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-config-provider": "^2.0.0",
-        "@smithy/util-middleware": "^2.0.8",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.495.0.tgz",
+      "integrity": "sha512-ZgixNb+8dWUvc42Uso2fh38U7W7wW4OESUmQIFQzYW58B1ylZ4xuq/mo0xSY5b5j6u/+pJadvlIpx/QYBafVHg==",
+      "dependencies": {
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-config-provider": "^2.2.0",
+        "@smithy/util-middleware": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -694,17 +696,17 @@
       }
     },
     "node_modules/@aws-sdk/s3-request-presigner": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.468.0.tgz",
-      "integrity": "sha512-B01d7O3LXaF+WWmDTUaALy/lYM7bMd/i3xJ0MvnypaFosUoE9qmWdq0+lGF2BUExCXe+uZ7ID0rtwXKNsHrExA==",
-      "dependencies": {
-        "@aws-sdk/signature-v4-multi-region": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@aws-sdk/util-format-url": "3.468.0",
-        "@smithy/middleware-endpoint": "^2.2.2",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.495.0.tgz",
+      "integrity": "sha512-k/m4DhDiNRbp6nz8Mf+OMnL8p9tHdUVsekMpwiBg9VuunhwHI9tw+kBrvN14C1iMpw6VEUUbYNGeoqT9hdd/Zg==",
+      "dependencies": {
+        "@aws-sdk/signature-v4-multi-region": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@aws-sdk/util-format-url": "3.495.0",
+        "@smithy/middleware-endpoint": "^2.4.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -712,15 +714,15 @@
       }
     },
     "node_modules/@aws-sdk/signature-v4-multi-region": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.468.0.tgz",
-      "integrity": "sha512-ADMWVrqUUjaiWmK7IcBuekOd8nNW6qV1G8ZM9Dgu2U7ezC4gzgZ3IFqZRcQXANX32EC1K3EpDx6fhPpOE/Unbg==",
-      "dependencies": {
-        "@aws-sdk/middleware-sdk-s3": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/signature-v4": "^2.0.0",
-        "@smithy/types": "^2.7.0",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.495.0.tgz",
+      "integrity": "sha512-+U9Gpdafo8MYp98eRTx5flIazRdHWWv3UJVSteOaJRA1yErdM0IlwJRZAF1Q1E7sqzDP6ed4OkzcMLkpVG/clA==",
+      "dependencies": {
+        "@aws-sdk/middleware-sdk-s3": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/signature-v4": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -728,46 +730,46 @@
       }
     },
     "node_modules/@aws-sdk/token-providers": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.468.0.tgz",
-      "integrity": "sha512-IpLbthZmFXotwtgkE1Bw4HcKjwpAsGM+6iTXs4amZJqllJClOgyV/sV5Cze+8AqanfCZoPIFTmXyg8LfJTYwbw==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.495.0.tgz",
+      "integrity": "sha512-1JSEx82FMKNNtPoV5NRpFxi0XHgfvonCKb4+2lR/k4nljqeysZPnOaIW/7C1eAwhoJ6buEIVxoHscemBtdKo+A==",
       "dependencies": {
         "@aws-crypto/sha256-browser": "3.0.0",
         "@aws-crypto/sha256-js": "3.0.0",
-        "@aws-sdk/middleware-host-header": "3.468.0",
-        "@aws-sdk/middleware-logger": "3.468.0",
-        "@aws-sdk/middleware-recursion-detection": "3.468.0",
-        "@aws-sdk/middleware-user-agent": "3.468.0",
-        "@aws-sdk/region-config-resolver": "3.468.0",
-        "@aws-sdk/types": "3.468.0",
-        "@aws-sdk/util-endpoints": "3.468.0",
-        "@aws-sdk/util-user-agent-browser": "3.468.0",
-        "@aws-sdk/util-user-agent-node": "3.468.0",
-        "@smithy/config-resolver": "^2.0.20",
-        "@smithy/fetch-http-handler": "^2.3.1",
-        "@smithy/hash-node": "^2.0.17",
-        "@smithy/invalid-dependency": "^2.0.15",
-        "@smithy/middleware-content-length": "^2.0.17",
-        "@smithy/middleware-endpoint": "^2.2.2",
-        "@smithy/middleware-retry": "^2.0.23",
-        "@smithy/middleware-serde": "^2.0.15",
-        "@smithy/middleware-stack": "^2.0.9",
-        "@smithy/node-config-provider": "^2.1.7",
-        "@smithy/node-http-handler": "^2.2.1",
-        "@smithy/property-provider": "^2.0.0",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/shared-ini-file-loader": "^2.0.6",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
-        "@smithy/url-parser": "^2.0.15",
-        "@smithy/util-base64": "^2.0.1",
-        "@smithy/util-body-length-browser": "^2.0.1",
-        "@smithy/util-body-length-node": "^2.1.0",
-        "@smithy/util-defaults-mode-browser": "^2.0.22",
-        "@smithy/util-defaults-mode-node": "^2.0.28",
-        "@smithy/util-endpoints": "^1.0.6",
-        "@smithy/util-retry": "^2.0.8",
-        "@smithy/util-utf8": "^2.0.2",
+        "@aws-sdk/middleware-host-header": "3.495.0",
+        "@aws-sdk/middleware-logger": "3.495.0",
+        "@aws-sdk/middleware-recursion-detection": "3.495.0",
+        "@aws-sdk/middleware-user-agent": "3.495.0",
+        "@aws-sdk/region-config-resolver": "3.495.0",
+        "@aws-sdk/types": "3.495.0",
+        "@aws-sdk/util-endpoints": "3.495.0",
+        "@aws-sdk/util-user-agent-browser": "3.495.0",
+        "@aws-sdk/util-user-agent-node": "3.495.0",
+        "@smithy/config-resolver": "^2.1.0",
+        "@smithy/fetch-http-handler": "^2.4.0",
+        "@smithy/hash-node": "^2.1.0",
+        "@smithy/invalid-dependency": "^2.1.0",
+        "@smithy/middleware-content-length": "^2.1.0",
+        "@smithy/middleware-endpoint": "^2.4.0",
+        "@smithy/middleware-retry": "^2.1.0",
+        "@smithy/middleware-serde": "^2.1.0",
+        "@smithy/middleware-stack": "^2.1.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/node-http-handler": "^2.3.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/shared-ini-file-loader": "^2.3.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/url-parser": "^2.1.0",
+        "@smithy/util-base64": "^2.1.0",
+        "@smithy/util-body-length-browser": "^2.1.0",
+        "@smithy/util-body-length-node": "^2.2.0",
+        "@smithy/util-defaults-mode-browser": "^2.1.0",
+        "@smithy/util-defaults-mode-node": "^2.1.0",
+        "@smithy/util-endpoints": "^1.1.0",
+        "@smithy/util-retry": "^2.1.0",
+        "@smithy/util-utf8": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -775,11 +777,11 @@
       }
     },
     "node_modules/@aws-sdk/types": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.468.0.tgz",
-      "integrity": "sha512-rx/9uHI4inRbp2tw3Y4Ih4PNZkVj32h7WneSg3MVgVjAoVD5Zti9KhS5hkvsBxfgmQmg0AQbE+b1sy5WGAgntA==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.495.0.tgz",
+      "integrity": "sha512-KUpo2U1rD4U6gT1QNPUJGmbQnruvIJmPeuyKndil6h2zkCpG5I0AHE8ixpfuBbizIZQOIA/26pArQivDChOD9A==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -787,9 +789,9 @@
       }
     },
     "node_modules/@aws-sdk/util-arn-parser": {
-      "version": "3.465.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.465.0.tgz",
-      "integrity": "sha512-zOJ82vzDJFqBX9yZBlNeHHrul/kpx/DCoxzW5UBbZeb26kfV53QhMSoEmY8/lEbBqlqargJ/sgRC845GFhHNQw==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.495.0.tgz",
+      "integrity": "sha512-hwdA3XAippSEUxs7jpznwD63YYFR+LtQvlEcebPTgWR9oQgG9TfS+39PUfbnEeje1ICuOrN3lrFqFbmP9uzbMg==",
       "dependencies": {
         "tslib": "^2.5.0"
       },
@@ -798,12 +800,13 @@
       }
     },
     "node_modules/@aws-sdk/util-endpoints": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.468.0.tgz",
-      "integrity": "sha512-P91EbMG2+1ZToJeTLaRkdO7qM7RI0svuMVLkIdHV9rHR7PeUKUWMpf46xh8rQsIjKC9Arf+I9ueWp3iHJt1T5w==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.495.0.tgz",
+      "integrity": "sha512-pMJ6rb16y51I4G33xtinkXAXH/2mQ0WZCwoh1sNkCM2MUfZDw9zAyP+PvB2tpEytQX8Fc7bR4qIP+td+pPEXAg==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/util-endpoints": "^1.0.6",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-endpoints": "^1.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -811,13 +814,13 @@
       }
     },
     "node_modules/@aws-sdk/util-format-url": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.468.0.tgz",
-      "integrity": "sha512-CtHApPmudJz/Z2MHVogWfkaSw4wWHQKVLQs4Q5XjvLcDSzODzxHbiOIckFCXQm2Mme4+TTe4GFU9g869ufegXg==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.495.0.tgz",
+      "integrity": "sha512-CmZcUoD2C+VSVvhAOpezkTyEiaGS31zJH8QNCvuKb/QA4yaZSdUrq4FUS9oxsVQr04MxjXVEfPZ427l0LTH1ow==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/querystring-builder": "^2.0.15",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/querystring-builder": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -825,9 +828,9 @@
       }
     },
     "node_modules/@aws-sdk/util-locate-window": {
-      "version": "3.465.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.465.0.tgz",
-      "integrity": "sha512-f+QNcWGswredzC1ExNAB/QzODlxwaTdXkNT5cvke2RLX8SFU5pYk6h4uCtWC0vWPELzOfMfloBrJefBzlarhsw==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz",
+      "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==",
       "dependencies": {
         "tslib": "^2.5.0"
       },
@@ -836,24 +839,24 @@
       }
     },
     "node_modules/@aws-sdk/util-user-agent-browser": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.468.0.tgz",
-      "integrity": "sha512-OJyhWWsDEizR3L+dCgMXSUmaCywkiZ7HSbnQytbeKGwokIhD69HTiJcibF/sgcM5gk4k3Mq3puUhGnEZ46GIig==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.495.0.tgz",
+      "integrity": "sha512-CIlY54aKahUqF4kygbMkDkFRc9t+8Km/r+IWapy91h0Exy84V+S47MJdAelsMg8Id6hZ47jWmuuzz5UcjU/+sQ==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/types": "^2.9.0",
         "bowser": "^2.11.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@aws-sdk/util-user-agent-node": {
-      "version": "3.468.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.468.0.tgz",
-      "integrity": "sha512-9p+Zyp6xmJUkcryTNmQQwdhRK6gAC6zVEJZLomLGQhD7sWcCzstolw//mAS3AKVQFYWnCEGKrDJdgT0KObCf4g==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.495.0.tgz",
+      "integrity": "sha512-BbEwwh9SCtMrcNES0u4q5/8BjAKkOiHGia0gDSlQHOmEzXxYvhx0ByRMaPeprL06iESFa6HcleJWenWktfxk3g==",
       "dependencies": {
-        "@aws-sdk/types": "3.468.0",
-        "@smithy/node-config-provider": "^2.1.7",
-        "@smithy/types": "^2.7.0",
+        "@aws-sdk/types": "3.495.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -877,10 +880,11 @@
       }
     },
     "node_modules/@aws-sdk/xml-builder": {
-      "version": "3.465.0",
-      "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.465.0.tgz",
-      "integrity": "sha512-9TKW5ZgsReygePTnAUdvaqxr/k1HXsEz2yDnk/jTLaUeRPsd5la8fFjb6OfgYYlbEVNlxTcKzaqOdrqxpUkmyQ==",
+      "version": "3.495.0",
+      "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.495.0.tgz",
+      "integrity": "sha512-bBrVLuldAnv53E9XvZD9MtW1dIWJXFswP8/JZuMdDQCyJh9ObjvUe/lFhTJ/AuNqEdujyE1nD4O1R7stzyBqOA==",
       "dependencies": {
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -888,9 +892,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz",
-      "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==",
+      "version": "7.23.6",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
+      "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -920,10 +924,26 @@
         "esbuild": "*"
       }
     },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz",
+      "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/@esbuild/android-arm": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.8.tgz",
-      "integrity": "sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz",
+      "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==",
       "cpu": [
         "arm"
       ],
@@ -932,15 +952,14 @@
       "os": [
         "android"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/android-arm64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.8.tgz",
-      "integrity": "sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz",
+      "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==",
       "cpu": [
         "arm64"
       ],
@@ -949,15 +968,14 @@
       "os": [
         "android"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/android-x64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.8.tgz",
-      "integrity": "sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz",
+      "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==",
       "cpu": [
         "x64"
       ],
@@ -966,15 +984,14 @@
       "os": [
         "android"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.8.tgz",
-      "integrity": "sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz",
+      "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==",
       "cpu": [
         "arm64"
       ],
@@ -983,15 +1000,14 @@
       "os": [
         "darwin"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/darwin-x64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.8.tgz",
-      "integrity": "sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz",
+      "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==",
       "cpu": [
         "x64"
       ],
@@ -1000,15 +1016,14 @@
       "os": [
         "darwin"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.8.tgz",
-      "integrity": "sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz",
+      "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==",
       "cpu": [
         "arm64"
       ],
@@ -1017,15 +1032,14 @@
       "os": [
         "freebsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.8.tgz",
-      "integrity": "sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz",
+      "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==",
       "cpu": [
         "x64"
       ],
@@ -1034,15 +1048,14 @@
       "os": [
         "freebsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-arm": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.8.tgz",
-      "integrity": "sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz",
+      "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==",
       "cpu": [
         "arm"
       ],
@@ -1051,15 +1064,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-arm64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.8.tgz",
-      "integrity": "sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz",
+      "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==",
       "cpu": [
         "arm64"
       ],
@@ -1068,15 +1080,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-ia32": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.8.tgz",
-      "integrity": "sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz",
+      "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==",
       "cpu": [
         "ia32"
       ],
@@ -1085,15 +1096,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-loong64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.8.tgz",
-      "integrity": "sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz",
+      "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==",
       "cpu": [
         "loong64"
       ],
@@ -1102,15 +1112,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.8.tgz",
-      "integrity": "sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz",
+      "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==",
       "cpu": [
         "mips64el"
       ],
@@ -1119,15 +1128,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.8.tgz",
-      "integrity": "sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz",
+      "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==",
       "cpu": [
         "ppc64"
       ],
@@ -1136,15 +1144,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.8.tgz",
-      "integrity": "sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz",
+      "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==",
       "cpu": [
         "riscv64"
       ],
@@ -1153,15 +1160,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-s390x": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.8.tgz",
-      "integrity": "sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz",
+      "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==",
       "cpu": [
         "s390x"
       ],
@@ -1170,15 +1176,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/linux-x64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz",
-      "integrity": "sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz",
+      "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==",
       "cpu": [
         "x64"
       ],
@@ -1187,15 +1192,14 @@
       "os": [
         "linux"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.8.tgz",
-      "integrity": "sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz",
+      "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==",
       "cpu": [
         "x64"
       ],
@@ -1204,15 +1208,14 @@
       "os": [
         "netbsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.8.tgz",
-      "integrity": "sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz",
+      "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==",
       "cpu": [
         "x64"
       ],
@@ -1221,15 +1224,14 @@
       "os": [
         "openbsd"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/sunos-x64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.8.tgz",
-      "integrity": "sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz",
+      "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==",
       "cpu": [
         "x64"
       ],
@@ -1238,15 +1240,14 @@
       "os": [
         "sunos"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/win32-arm64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.8.tgz",
-      "integrity": "sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz",
+      "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==",
       "cpu": [
         "arm64"
       ],
@@ -1255,15 +1256,14 @@
       "os": [
         "win32"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/win32-ia32": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.8.tgz",
-      "integrity": "sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz",
+      "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==",
       "cpu": [
         "ia32"
       ],
@@ -1272,15 +1272,14 @@
       "os": [
         "win32"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
     },
     "node_modules/@esbuild/win32-x64": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.8.tgz",
-      "integrity": "sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz",
+      "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==",
       "cpu": [
         "x64"
       ],
@@ -1289,7 +1288,6 @@
       "os": [
         "win32"
       ],
-      "peer": true,
       "engines": {
         "node": ">=12"
       }
@@ -1382,13 +1380,13 @@
       }
     },
     "node_modules/@humanwhocodes/config-array": {
-      "version": "0.11.13",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
-      "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
+      "version": "0.11.14",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+      "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
       "dev": true,
       "dependencies": {
-        "@humanwhocodes/object-schema": "^2.0.1",
-        "debug": "^4.1.1",
+        "@humanwhocodes/object-schema": "^2.0.2",
+        "debug": "^4.3.1",
         "minimatch": "^3.0.5"
       },
       "engines": {
@@ -1409,9 +1407,9 @@
       }
     },
     "node_modules/@humanwhocodes/object-schema": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
-      "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
+      "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
       "dev": true
     },
     "node_modules/@jridgewell/sourcemap-codec": {
@@ -1465,19 +1463,11 @@
         "node": ">= 8"
       }
     },
-    "node_modules/@pkgr/utils": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz",
-      "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==",
+    "node_modules/@pkgr/core": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+      "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
       "dev": true,
-      "dependencies": {
-        "cross-spawn": "^7.0.3",
-        "fast-glob": "^3.3.0",
-        "is-glob": "^4.0.3",
-        "open": "^9.1.0",
-        "picocolors": "^1.0.0",
-        "tslib": "^2.6.0"
-      },
       "engines": {
         "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
       },
@@ -1494,6 +1484,175 @@
         "url": "https://opencollective.com/popperjs"
       }
     },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz",
+      "integrity": "sha512-idWaG8xeSRCfRq9KpRysDHJ/rEHBEXcHuJ82XY0yYFIWnLMjZv9vF/7DOq8djQ2n3Lk6+3qfSH8AqlmHlmi1MA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.5.tgz",
+      "integrity": "sha512-f14d7uhAMtsCGjAYwZGv6TwuS3IFaM4ZnGMUn3aCBgkcHAYErhV1Ad97WzBvS2o0aaDv4mVz+syiN0ElMyfBPg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.5.tgz",
+      "integrity": "sha512-ndoXeLx455FffL68OIUrVr89Xu1WLzAG4n65R8roDlCoYiQcGGg6MALvs2Ap9zs7AHg8mpHtMpwC8jBBjZrT/w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.5.tgz",
+      "integrity": "sha512-UmElV1OY2m/1KEEqTlIjieKfVwRg0Zwg4PLgNf0s3glAHXBN99KLpw5A5lrSYCa1Kp63czTpVll2MAqbZYIHoA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.5.tgz",
+      "integrity": "sha512-Q0LcU61v92tQB6ae+udZvOyZ0wfpGojtAKrrpAaIqmJ7+psq4cMIhT/9lfV6UQIpeItnq/2QDROhNLo00lOD1g==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.5.tgz",
+      "integrity": "sha512-dkRscpM+RrR2Ee3eOQmRWFjmV/payHEOrjyq1VZegRUa5OrZJ2MAxBNs05bZuY0YCtpqETDy1Ix4i/hRqX98cA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.5.tgz",
+      "integrity": "sha512-QaKFVOzzST2xzY4MAmiDmURagWLFh+zZtttuEnuNn19AiZ0T3fhPyjPPGwLNdiDT82ZE91hnfJsUiDwF9DClIQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.5.tgz",
+      "integrity": "sha512-HeGqmRJuyVg6/X6MpE2ur7GbymBPS8Np0S/vQFHDmocfORT+Zt76qu+69NUoxXzGqVP1pzaY6QIi0FJWLC3OPA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz",
+      "integrity": "sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.5.tgz",
+      "integrity": "sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.5.tgz",
+      "integrity": "sha512-aHSsMnUw+0UETB0Hlv7B/ZHOGY5bQdwMKJSzGfDfvyhnpmVxLMGnQPGNE9wgqkLUs3+gbG1Qx02S2LLfJ5GaRQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.5.tgz",
+      "integrity": "sha512-AiqiLkb9KSf7Lj/o1U3SEP9Zn+5NuVKgFdRIZkvd4N0+bYrTOovVd0+LmYCPQGbocT4kvFyK+LXCDiXPBF3fyA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.5.tgz",
+      "integrity": "sha512-1q+mykKE3Vot1kaFJIDoUFv5TuW+QQVaf2FmTT9krg86pQrGStOSJJ0Zil7CFagyxDuouTepzt5Y5TVzyajOdQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
     "node_modules/@rushstack/eslint-patch": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
@@ -1501,11 +1660,11 @@
       "dev": true
     },
     "node_modules/@smithy/abort-controller": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.15.tgz",
-      "integrity": "sha512-JkS36PIS3/UCbq/MaozzV7jECeL+BTt4R75bwY8i+4RASys4xOyUS1HsRyUNSqUXFP4QyCz5aNnh3ltuaxv+pw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.0.tgz",
+      "integrity": "sha512-fyPlWpzXyKzDVRRMUbsfH7AV/2xX+dyZ5RqeEo6Hjz9YUvDMGVSnm88iHH0zqZ+XmH4+sH4+mhwRL76HXX65uw==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1513,31 +1672,49 @@
       }
     },
     "node_modules/@smithy/chunked-blob-reader": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.0.0.tgz",
-      "integrity": "sha512-k+J4GHJsMSAIQPChGBrjEmGS+WbPonCXesoqP9fynIqjn7rdOThdH8FAeCmokP9mxTYKQAKoHCLPzNlm6gh7Wg==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.1.0.tgz",
+      "integrity": "sha512-meKoKCIXxixSGzUGVXGc1lnn6cEM21XzknDfUmHopPCaYSgt86w3gaJSua8Gr3VYcSkkMTW2MyAygTXprLEOZQ==",
       "dependencies": {
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/chunked-blob-reader-native": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.0.1.tgz",
-      "integrity": "sha512-N2oCZRglhWKm7iMBu7S6wDzXirjAofi7tAd26cxmgibRYOBS4D3hGfmkwCpHdASZzwZDD8rluh0Rcqw1JeZDRw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.1.0.tgz",
+      "integrity": "sha512-r9fRVRvQXpuWZtHX3VNAP4PQoCXvRDqcwr15TbaKSdtEJ/f0IPHDQ+M2MOEsYt2234FkNqCzAqtmeJrjpNak2g==",
       "dependencies": {
-        "@smithy/util-base64": "^2.0.1",
+        "@smithy/util-base64": "^2.1.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/config-resolver": {
-      "version": "2.0.21",
-      "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.21.tgz",
-      "integrity": "sha512-rlLIGT+BeqjnA6C2FWumPRJS1UW07iU5ZxDHtFuyam4W65gIaOFMjkB90ofKCIh+0mLVQrQFrl/VLtQT/6FWTA==",
-      "dependencies": {
-        "@smithy/node-config-provider": "^2.1.8",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-config-provider": "^2.0.0",
-        "@smithy/util-middleware": "^2.0.8",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.0.tgz",
+      "integrity": "sha512-NcR1Hw2uZgwHT7/KFsQH76YHb/mNGLFu+hS0ODnoFUpViE8ddIVOXm/8sgwdh0QvFPtWGzPn0Wcp19Cm31wv2A==",
+      "dependencies": {
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-config-provider": "^2.2.0",
+        "@smithy/util-middleware": "^2.1.0",
+        "tslib": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@smithy/core": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.0.tgz",
+      "integrity": "sha512-XoU9eiICwhxZIyAdugijyD/YqsumDQ3FgGyFSJibO60qoUkdfMGSjnIvrTemjFBdnDsj4B26F/ZRxSR3PUJbJQ==",
+      "dependencies": {
+        "@smithy/middleware-endpoint": "^2.4.0",
+        "@smithy/middleware-retry": "^2.1.0",
+        "@smithy/middleware-serde": "^2.1.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-middleware": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1545,14 +1722,14 @@
       }
     },
     "node_modules/@smithy/credential-provider-imds": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.4.tgz",
-      "integrity": "sha512-cwPJN1fa1YOQzhBlTXRavABEYRRchci1X79QRwzaNLySnIMJfztyv1Zkst0iZPLMnpn8+CnHu3wOHS11J5Dr3A==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.0.tgz",
+      "integrity": "sha512-uqoRizHR8rKih6SuWcJRSv46tdqZk1zPEk6r909O87XO85j21MfUcxRKzbkORM2JOlaFhCH4geRcvlvYfK6EyQ==",
       "dependencies": {
-        "@smithy/node-config-provider": "^2.1.8",
-        "@smithy/property-provider": "^2.0.16",
-        "@smithy/types": "^2.7.0",
-        "@smithy/url-parser": "^2.0.15",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/url-parser": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1560,23 +1737,23 @@
       }
     },
     "node_modules/@smithy/eventstream-codec": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.15.tgz",
-      "integrity": "sha512-crjvz3j1gGPwA0us6cwS7+5gAn35CTmqu/oIxVbYJo2Qm/sGAye6zGJnMDk3BKhWZw5kcU1G4MxciTkuBpOZPg==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.0.tgz",
+      "integrity": "sha512-1yQnf8bSycsZ5ICXVMf8pEj1DQSUsw6/3H4nEdzH2+E3RZdNGPjVecQEm9kWPW7fvXvNvzT8MvZOQdk1IWoVTg==",
       "dependencies": {
         "@aws-crypto/crc32": "3.0.0",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-hex-encoding": "^2.0.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-hex-encoding": "^2.1.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/eventstream-serde-browser": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.0.15.tgz",
-      "integrity": "sha512-WiFG5N9j3jmS5P0z5Xev6dO0c3lf7EJYC2Ncb0xDnWFvShwXNn741AF71ABr5EcZw8F4rQma0362MMjAwJeZog==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.1.0.tgz",
+      "integrity": "sha512-pMw3HGN8yTGGoAO8z/fOMSSsfJxdtEwQ9p4/Y1eYw07sMlgQUPadwYFtxTMPDDzYvNmTWFjspR/nTBxYiUe8nA==",
       "dependencies": {
-        "@smithy/eventstream-serde-universal": "^2.0.15",
-        "@smithy/types": "^2.7.0",
+        "@smithy/eventstream-serde-universal": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1584,11 +1761,11 @@
       }
     },
     "node_modules/@smithy/eventstream-serde-config-resolver": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.0.15.tgz",
-      "integrity": "sha512-o65d2LRjgCbWYH+VVNlWXtmsI231SO99ZTOL4UuIPa6WTjbSHWtlXvUcJG9libhEKWmEV9DIUiH2IqyPWi7ubA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.1.0.tgz",
+      "integrity": "sha512-tFhaEiJtitNmdyW6yLteh0EV+93EsV+CIb4yduwpL/WyMy7Hy7DLbRW5ImypA4auqebjWYBven876RjhpY6XLg==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1596,12 +1773,12 @@
       }
     },
     "node_modules/@smithy/eventstream-serde-node": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.0.15.tgz",
-      "integrity": "sha512-9OOXiIhHq1VeOG6xdHkn2ZayfMYM3vzdUTV3zhcCnt+tMqA3BJK3XXTJFRR2BV28rtRM778DzqbBTf+hqwQPTg==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.1.0.tgz",
+      "integrity": "sha512-/asga1STbTgxQ+ma/VfsjXlUHTH/Fofor4RZLhPAMpQ6lfVxJTRjm28ONSczcsnRPTWwOoiFBiXutM68WgK6IQ==",
       "dependencies": {
-        "@smithy/eventstream-serde-universal": "^2.0.15",
-        "@smithy/types": "^2.7.0",
+        "@smithy/eventstream-serde-universal": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1609,12 +1786,12 @@
       }
     },
     "node_modules/@smithy/eventstream-serde-universal": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.0.15.tgz",
-      "integrity": "sha512-dP8AQp/pXlWBjvL0TaPBJC3rM0GoYv7O0Uim8d/7UKZ2Wo13bFI3/BhQfY/1DeiP1m23iCHFNFtOQxfQNBB8rQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.1.0.tgz",
+      "integrity": "sha512-kZtTF0llc5pZ2QLMOrLttA2Cde/DXanfMqBhtJ0VZaQHdntPon+d7Gx7GhOkCxDP4lz1u0wMLdiIZNduaA4Qbg==",
       "dependencies": {
-        "@smithy/eventstream-codec": "^2.0.15",
-        "@smithy/types": "^2.7.0",
+        "@smithy/eventstream-codec": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1622,36 +1799,36 @@
       }
     },
     "node_modules/@smithy/fetch-http-handler": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.3.1.tgz",
-      "integrity": "sha512-6MNk16fqb8EwcYY8O8WxB3ArFkLZ2XppsSNo1h7SQcFdDDwIumiJeO6wRzm7iB68xvsOQzsdQKbdtTieS3hfSQ==",
-      "dependencies": {
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/querystring-builder": "^2.0.15",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-base64": "^2.0.1",
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.0.tgz",
+      "integrity": "sha512-fLhPNfbWG8vTcS9PsR1wjHaA54kDcSiAZKVuVAfjHleS7QDWjrCr1SDUqCB2yAc9NBLe2lIDbDL8+i9yoYhxoQ==",
+      "dependencies": {
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/querystring-builder": "^2.1.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-base64": "^2.1.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/hash-blob-browser": {
-      "version": "2.0.16",
-      "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-2.0.16.tgz",
-      "integrity": "sha512-cSYRi05LA7DZDwjB1HL0BP8B56eUNNeLglVH147QTXFyuXJq/7erAIiLRfsyXB8+GfFHkSS5BHbc76a7k/AYPA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-2.1.0.tgz",
+      "integrity": "sha512-MVlH6algsOuEaK745oSoymk7Tusny7AqP2bQ1yPzxJiWpHirHnzEzYP/aqZaZ4gWdSLMFF65WOwL6q2ijuKVgA==",
       "dependencies": {
-        "@smithy/chunked-blob-reader": "^2.0.0",
-        "@smithy/chunked-blob-reader-native": "^2.0.1",
-        "@smithy/types": "^2.7.0",
+        "@smithy/chunked-blob-reader": "^2.1.0",
+        "@smithy/chunked-blob-reader-native": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/hash-node": {
-      "version": "2.0.17",
-      "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.17.tgz",
-      "integrity": "sha512-Il6WuBcI1nD+e2DM7tTADMf01wEPGK8PAhz4D+YmDUVaoBqlA+CaH2uDJhiySifmuKBZj748IfygXty81znKhw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.0.tgz",
+      "integrity": "sha512-/B7b6NNjw+i4PlwsrYHmxmmrTxp2oRejgZH26HhXE77XWwAiPEI9iHu7GZR9fYhm7Fsj66Z9Bk6JA9aEvUC9/w==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-buffer-from": "^2.0.0",
-        "@smithy/util-utf8": "^2.0.2",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-buffer-from": "^2.1.0",
+        "@smithy/util-utf8": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1659,12 +1836,12 @@
       }
     },
     "node_modules/@smithy/hash-stream-node": {
-      "version": "2.0.17",
-      "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-2.0.17.tgz",
-      "integrity": "sha512-ey8DtnATzp1mOXgS7rqMwSmAki6iJA+jgNucKcxRkhMB1rrICfHg+rhmIF50iLPDHUhTcS5pBMOrLzzpZftvNQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-2.1.0.tgz",
+      "integrity": "sha512-qhgWuXt8sVcDKrFNBRQmcIo6wfzONdeKlKDLsau4kKZ7xlEHScgUFtsAHvspV8sVREJIeMbOq4oSFSVmzvOikQ==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-utf8": "^2.0.2",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-utf8": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1672,18 +1849,18 @@
       }
     },
     "node_modules/@smithy/invalid-dependency": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.15.tgz",
-      "integrity": "sha512-dlEKBFFwVfzA5QroHlBS94NpgYjXhwN/bFfun+7w3rgxNvVy79SK0w05iGc7UAeC5t+D7gBxrzdnD6hreZnDVQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.0.tgz",
+      "integrity": "sha512-hvryGI0KChV4jMgK/kwr6U4/HaYldzjiQAZ+c//QAMDoCp0KkP0Xt94XqAkr7Uq08577mAMW5U70YCaAx+KjSQ==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/is-array-buffer": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz",
-      "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.0.tgz",
+      "integrity": "sha512-XnQvn/6ie5kjFyeW94NqSjGGOdMuB2WnNmDWKHHLVMCR/Emu7B8pcAZX4k8H3tjDujXAQvfBrEgmPRq6FgqmZg==",
       "dependencies": {
         "tslib": "^2.5.0"
       },
@@ -1692,22 +1869,22 @@
       }
     },
     "node_modules/@smithy/md5-js": {
-      "version": "2.0.17",
-      "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.0.17.tgz",
-      "integrity": "sha512-jmISTCnEkOnm2oCNx/rMkvBT/eQh3aA6nktevkzbmn/VYqYEuc5Z2n5sTTqsciMSO01Lvf56wG1A4twDqovYeQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.1.0.tgz",
+      "integrity": "sha512-pl0lDIn4i+J2aI2gqlCIsOczPRi+YtXS9noQ/KXMUCqapb6AWomRDAloBBxRTClBFHIV6ife9UQrOhLT/Y+Yrw==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-utf8": "^2.0.2",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-utf8": "^2.1.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/middleware-content-length": {
-      "version": "2.0.17",
-      "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.17.tgz",
-      "integrity": "sha512-OyadvMcKC7lFXTNBa8/foEv7jOaqshQZkjWS9coEXPRZnNnihU/Ls+8ZuJwGNCOrN2WxXZFmDWhegbnM4vak8w==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.0.tgz",
+      "integrity": "sha512-XYhKZPuS8nnecdx0IGGUt1Nt2/ekoVOw1zal4c0ARRaLJEw+umFLxwHUelIeBocbdOcPCeZRE6pdk35Y2T2wpw==",
       "dependencies": {
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/types": "^2.7.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1715,16 +1892,16 @@
       }
     },
     "node_modules/@smithy/middleware-endpoint": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.2.3.tgz",
-      "integrity": "sha512-nYfxuq0S/xoAjdLbyn1ixeVB6cyH9wYCMtbbOCpcCRYR5u2mMtqUtVjjPAZ/DIdlK3qe0tpB0Q76szFGNuz+kQ==",
-      "dependencies": {
-        "@smithy/middleware-serde": "^2.0.15",
-        "@smithy/node-config-provider": "^2.1.8",
-        "@smithy/shared-ini-file-loader": "^2.2.7",
-        "@smithy/types": "^2.7.0",
-        "@smithy/url-parser": "^2.0.15",
-        "@smithy/util-middleware": "^2.0.8",
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.0.tgz",
+      "integrity": "sha512-GMebLCihCxIlbPdA/l6WDpNJppIgW5OeTJkIAbqVArg1vFxZ92XhW+UwN12av5OAXswySGJ80/fpDFP7HmSyYg==",
+      "dependencies": {
+        "@smithy/middleware-serde": "^2.1.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/shared-ini-file-loader": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/url-parser": "^2.1.0",
+        "@smithy/util-middleware": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1732,17 +1909,17 @@
       }
     },
     "node_modules/@smithy/middleware-retry": {
-      "version": "2.0.24",
-      "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.24.tgz",
-      "integrity": "sha512-q2SvHTYu96N7lYrn3VSuX3vRpxXHR/Cig6MJpGWxd0BWodUQUWlKvXpWQZA+lTaFJU7tUvpKhRd4p4MU3PbeJg==",
-      "dependencies": {
-        "@smithy/node-config-provider": "^2.1.8",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/service-error-classification": "^2.0.8",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-middleware": "^2.0.8",
-        "@smithy/util-retry": "^2.0.8",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.0.tgz",
+      "integrity": "sha512-lGEVds90hFyIAvypH58rwC6j9mrCR2ZwYbcxow7AgW6sWCCoBppz5FtLpgSg6QV/CTRh8K7w4kxGVx8LqINQBg==",
+      "dependencies": {
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/service-error-classification": "^2.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-middleware": "^2.1.0",
+        "@smithy/util-retry": "^2.1.0",
         "tslib": "^2.5.0",
         "uuid": "^8.3.2"
       },
@@ -1751,11 +1928,11 @@
       }
     },
     "node_modules/@smithy/middleware-serde": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.15.tgz",
-      "integrity": "sha512-FOZRFk/zN4AT4wzGuBY+39XWe+ZnCFd0gZtyw3f9Okn2CJPixl9GyWe98TIaljeZdqWkgrzGyPre20AcW2UMHQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.0.tgz",
+      "integrity": "sha512-iysAUIDKsc354HMnYVQxMJEzNaOrQQvE86b1oSl2fRwcFqn+9TTi028a37PLFE+ccAiyVGjBjB8PBsAz9plUug==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1763,11 +1940,11 @@
       }
     },
     "node_modules/@smithy/middleware-stack": {
-      "version": "2.0.9",
-      "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.9.tgz",
-      "integrity": "sha512-bCB5dUtGQ5wh7QNL2ELxmDc6g7ih7jWU3Kx6MYH1h4mZbv9xL3WyhKHojRltThCB1arLPyTUFDi+x6fB/oabtA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.0.tgz",
+      "integrity": "sha512-y5Ph/TWfO7oTfxNqKU+uAK5cFRTYeP16ReOmDweq+zQ8NQODDg7LSxsfQT4Wp0mhIvm0bt3pZp66T1YMtnihWw==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1775,13 +1952,13 @@
       }
     },
     "node_modules/@smithy/node-config-provider": {
-      "version": "2.1.8",
-      "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.8.tgz",
-      "integrity": "sha512-+w26OKakaBUGp+UG+dxYZtFb5fs3tgHg3/QrRrmUZj+rl3cIuw840vFUXX35cVPTUCQIiTqmz7CpVF7+hdINdQ==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.0.tgz",
+      "integrity": "sha512-rU82PFR32Bxo4EMGUJ2BGG+K97zUp9j6SWjG83T2itmbXwA/+DoCc4xCON8kcmdej822x1yLcSzFiTeg0b472w==",
       "dependencies": {
-        "@smithy/property-provider": "^2.0.16",
-        "@smithy/shared-ini-file-loader": "^2.2.7",
-        "@smithy/types": "^2.7.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/shared-ini-file-loader": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1789,14 +1966,14 @@
       }
     },
     "node_modules/@smithy/node-http-handler": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.2.1.tgz",
-      "integrity": "sha512-8iAKQrC8+VFHPAT8pg4/j6hlsTQh+NKOWlctJBrYtQa4ExcxX7aSg3vdQ2XLoYwJotFUurg/NLqFCmZaPRrogw==",
-      "dependencies": {
-        "@smithy/abort-controller": "^2.0.15",
-        "@smithy/protocol-http": "^3.0.11",
-        "@smithy/querystring-builder": "^2.0.15",
-        "@smithy/types": "^2.7.0",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.3.0.tgz",
+      "integrity": "sha512-8jcQaOdrD/X0VihhM2W/KtJ5fvKaT8UpNf/pl/epvLQ6MkAttIMaCLex6xk31BpFSPvS2+q65ZdBBjQ3cMOSiA==",
+      "dependencies": {
+        "@smithy/abort-controller": "^2.1.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/querystring-builder": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1804,11 +1981,11 @@
       }
     },
     "node_modules/@smithy/property-provider": {
-      "version": "2.0.16",
-      "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.16.tgz",
-      "integrity": "sha512-28Ky0LlOqtEjwg5CdHmwwaDRHcTWfPRzkT6HrhwOSRS2RryAvuDfJrZpM+BMcrdeCyEg1mbcgIMoqTla+rdL8Q==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.0.tgz",
+      "integrity": "sha512-6cpCSsgwbKHnl567SrthpqLgZ7e5jc7qPHG6wz9U2T24vcUp2yiG0vdAlH1QdTH20+/PGamKR0ZM35a08X1Tbg==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1816,11 +1993,11 @@
       }
     },
     "node_modules/@smithy/protocol-http": {
-      "version": "3.0.11",
-      "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.11.tgz",
-      "integrity": "sha512-3ziB8fHuXIRamV/akp/sqiWmNPR6X+9SB8Xxnozzj+Nq7hSpyKdFHd1FLpBkgfGFUTzzcBJQlDZPSyxzmdcx5A==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.1.0.tgz",
+      "integrity": "sha512-CGNzkKza1yUga7sv+U4gx3jbwSh5x42/9vy0E/NoR2HTFken2MuMc/bClxXAO0Z6EQoTYHHA6FMCREXwSP04lg==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1828,12 +2005,12 @@
       }
     },
     "node_modules/@smithy/querystring-builder": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.15.tgz",
-      "integrity": "sha512-e1q85aT6HutvouOdN+dMsN0jcdshp50PSCvxDvo6aIM57LqeXimjfONUEgfqQ4IFpYWAtVixptyIRE5frMp/2A==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.0.tgz",
+      "integrity": "sha512-8QColSkqn9TbvpX40zW0T8IrKcLXg7Um4bczm9qIYDRPh8T873WNIOWzYBw8chI8SWizMXbsSR95PFCP/YlgYw==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-uri-escape": "^2.0.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-uri-escape": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1841,11 +2018,11 @@
       }
     },
     "node_modules/@smithy/querystring-parser": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.15.tgz",
-      "integrity": "sha512-jbBvoK3cc81Cj1c1TH1qMYxNQKHrYQ2DoTntN9FBbtUWcGhc+T4FP6kCKYwRLXyU4AajwGIZstvNAmIEgUUNTQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.0.tgz",
+      "integrity": "sha512-+l17LQQxelslo5CHsLXwSw2F1J6Qmf64OgByreNnLR82gHkJ91ZbMFhxZeLTo2qXxEu0uqraMc4uNw8qE9A6bw==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1853,22 +2030,22 @@
       }
     },
     "node_modules/@smithy/service-error-classification": {
-      "version": "2.0.8",
-      "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.8.tgz",
-      "integrity": "sha512-jCw9+005im8tsfYvwwSc4TTvd29kXRFkH9peQBg5R/4DD03ieGm6v6Hpv9nIAh98GwgYg1KrztcINC1s4o7/hg==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.0.tgz",
+      "integrity": "sha512-yBMJk4IfYqUxsPmc8P0YtWHd/Kbd0PP+kU0dgFksH6eiE2ZQJl7478xNtkUKp2QJLcooYEbA3gBFUza6ukXMiA==",
       "dependencies": {
-        "@smithy/types": "^2.7.0"
+        "@smithy/types": "^2.9.0"
       },
       "engines": {
         "node": ">=14.0.0"
       }
     },
     "node_modules/@smithy/shared-ini-file-loader": {
-      "version": "2.2.7",
-      "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.7.tgz",
-      "integrity": "sha512-0Qt5CuiogIuvQIfK+be7oVHcPsayLgfLJGkPlbgdbl0lD28nUKu4p11L+UG3SAEsqc9UsazO+nErPXw7+IgDpQ==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.0.tgz",
+      "integrity": "sha512-jgm7cjj0d08jIB9cp4idtpIUY590Twecv4xpijgl2IzkrPfBddzKTH4Zk+Zwfyk8ecz2T/7ihqtnNcq7Qdj9lw==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1876,17 +2053,17 @@
       }
     },
     "node_modules/@smithy/signature-v4": {
-      "version": "2.0.17",
-      "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.17.tgz",
-      "integrity": "sha512-ru5IUbHUAYgJ5ZqZaBi6PEsMjFT/do0Eu21Qt7b07NuRuPlwAMhlqNRDy/KE9QAF20ygehb+xe9ebmyZ26/BSA==",
-      "dependencies": {
-        "@smithy/eventstream-codec": "^2.0.15",
-        "@smithy/is-array-buffer": "^2.0.0",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-hex-encoding": "^2.0.0",
-        "@smithy/util-middleware": "^2.0.8",
-        "@smithy/util-uri-escape": "^2.0.0",
-        "@smithy/util-utf8": "^2.0.2",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.0.tgz",
+      "integrity": "sha512-ONi89MBjxNtl497obaO/qGixsOedikTV3CAj3ZBPGY3IKykS8wQ2Wkctsx2T1J5B9OnynH0KuGGmgG91utX/7w==",
+      "dependencies": {
+        "@smithy/eventstream-codec": "^2.1.0",
+        "@smithy/is-array-buffer": "^2.1.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-hex-encoding": "^2.1.0",
+        "@smithy/util-middleware": "^2.1.0",
+        "@smithy/util-uri-escape": "^2.1.0",
+        "@smithy/util-utf8": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1894,13 +2071,15 @@
       }
     },
     "node_modules/@smithy/smithy-client": {
-      "version": "2.1.18",
-      "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.18.tgz",
-      "integrity": "sha512-7FqdbaJiVaHJDD9IfDhmzhSDbpjyx+ZsfdYuOpDJF09rl8qlIAIlZNoSaflKrQ3cEXZN2YxGPaNWGhbYimyIRQ==",
-      "dependencies": {
-        "@smithy/middleware-stack": "^2.0.9",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-stream": "^2.0.23",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.3.0.tgz",
+      "integrity": "sha512-oEaLdVmHcbdK8IHQ4yE7xOYK2nSkF2xXp6nRr5NhfKB5QTKNzpNsXLiGJgfmm7j0ol1S6BhjyBhi7tZ8M0JJtg==",
+      "dependencies": {
+        "@smithy/middleware-endpoint": "^2.4.0",
+        "@smithy/middleware-stack": "^2.1.0",
+        "@smithy/protocol-http": "^3.1.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-stream": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1908,9 +2087,9 @@
       }
     },
     "node_modules/@smithy/types": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.7.0.tgz",
-      "integrity": "sha512-1OIFyhK+vOkMbu4aN2HZz/MomREkrAC/HqY5mlJMUJfGrPRwijJDTeiN8Rnj9zUaB8ogXAfIOtZrrgqZ4w7Wnw==",
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.9.0.tgz",
+      "integrity": "sha512-ST1M87Lf2cLHRI+irEFRIHXGY08HHTAUbiRFYkmFyJdTMg3VDxkcm7DwW9/EgV3X8M6wDPrbIkx/RXONyttrQg==",
       "dependencies": {
         "tslib": "^2.5.0"
       },
@@ -1919,21 +2098,21 @@
       }
     },
     "node_modules/@smithy/url-parser": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.15.tgz",
-      "integrity": "sha512-sADUncUj9rNbOTrdDGm4EXlUs0eQ9dyEo+V74PJoULY4jSQxS+9gwEgsPYyiu8PUOv16JC/MpHonOgqP/IEDZA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.0.tgz",
+      "integrity": "sha512-V3FMzNFCDwQNAgJdxI6Gj48qP9WAyvK59WE90hOoya3m8ey02uLDhWjZkl+505s7iTVVmJ7Mr7nKwG5vU2NIMQ==",
       "dependencies": {
-        "@smithy/querystring-parser": "^2.0.15",
-        "@smithy/types": "^2.7.0",
+        "@smithy/querystring-parser": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/util-base64": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz",
-      "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.0.tgz",
+      "integrity": "sha512-zjXlHFm7S+TEDVA3j1rWGpuNDTlTxIWDqzwIfWUENT0VqCGDAdJITd8RYVjduf3u8HWMlgALkrY6B62UTESQ5w==",
       "dependencies": {
-        "@smithy/util-buffer-from": "^2.0.0",
+        "@smithy/util-buffer-from": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1941,17 +2120,17 @@
       }
     },
     "node_modules/@smithy/util-body-length-browser": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.1.tgz",
-      "integrity": "sha512-NXYp3ttgUlwkaug4bjBzJ5+yIbUbUx8VsSLuHZROQpoik+gRkIBeEG9MPVYfvPNpuXb/puqodeeUXcKFe7BLOQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.0.tgz",
+      "integrity": "sha512-fkLY8W+jXGSkymLNe9NB7u6lGflHz6w1R+a3RxLOK6UrtwU4LBLskAP5Ag/zVPUNd5tmfv3/W6cTVzk8IBJuiw==",
       "dependencies": {
         "tslib": "^2.5.0"
       }
     },
     "node_modules/@smithy/util-body-length-node": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz",
-      "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.0.tgz",
+      "integrity": "sha512-ZLsqYH+s71y6Oc2Auws6zYI4LzsSi6N8+W+Gq7CwXaZm7QIKGiCeEunEwxo50OGAqJs0g6F9kCIwNxhlK1s4Aw==",
       "dependencies": {
         "tslib": "^2.5.0"
       },
@@ -1960,11 +2139,11 @@
       }
     },
     "node_modules/@smithy/util-buffer-from": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz",
-      "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.0.tgz",
+      "integrity": "sha512-3w7AM0moGyBmr9gMBGE7+pqG3cjboRvmMyRhpesbJoOUHO0BV1Qrk00M/wQ3EHJAQXM3dehQfFNUf7sR6nT6+Q==",
       "dependencies": {
-        "@smithy/is-array-buffer": "^2.0.0",
+        "@smithy/is-array-buffer": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -1972,9 +2151,9 @@
       }
     },
     "node_modules/@smithy/util-config-provider": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz",
-      "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.0.tgz",
+      "integrity": "sha512-D3Gx0BWXjsn1E25ikUt0+yc8oZnViTa5IHZ1JvD9J1NyyVS4c3IgHqbG64XRverEMnhzUb0EhqMTwQTY12in+w==",
       "dependencies": {
         "tslib": "^2.5.0"
       },
@@ -1983,13 +2162,13 @@
       }
     },
     "node_modules/@smithy/util-defaults-mode-browser": {
-      "version": "2.0.22",
-      "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.22.tgz",
-      "integrity": "sha512-qcF20IHHH96FlktvBRICDXDhLPtpVmtksHmqNGtotb9B0DYWXsC6jWXrkhrrwF7tH26nj+npVTqh9isiFV1gdA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.0.tgz",
+      "integrity": "sha512-zmXL4aKeBGBz02kDZdks2QfG+HGq99Tp4/ICPmu2OvSbwTOLjmlCnUrtZJTmLhX4etP3o0voOL9gFEa2PSjlJg==",
       "dependencies": {
-        "@smithy/property-provider": "^2.0.16",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "bowser": "^2.11.0",
         "tslib": "^2.5.0"
       },
@@ -1998,16 +2177,16 @@
       }
     },
     "node_modules/@smithy/util-defaults-mode-node": {
-      "version": "2.0.29",
-      "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.29.tgz",
-      "integrity": "sha512-+uG/15VoUh6JV2fdY9CM++vnSuMQ1VKZ6BdnkUM7R++C/vLjnlg+ToiSR1FqKZbMmKBXmsr8c/TsDWMAYvxbxQ==",
-      "dependencies": {
-        "@smithy/config-resolver": "^2.0.21",
-        "@smithy/credential-provider-imds": "^2.1.4",
-        "@smithy/node-config-provider": "^2.1.8",
-        "@smithy/property-provider": "^2.0.16",
-        "@smithy/smithy-client": "^2.1.18",
-        "@smithy/types": "^2.7.0",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.1.0.tgz",
+      "integrity": "sha512-pVBaw2fBJMjjJj+AR69xQhjzYLZ5u9azdKyaAAjR16dthdBOcnczBClBVCfhb/Moj0ivIHnaXJ5AXCdbDok94g==",
+      "dependencies": {
+        "@smithy/config-resolver": "^2.1.0",
+        "@smithy/credential-provider-imds": "^2.2.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/property-provider": "^2.1.0",
+        "@smithy/smithy-client": "^2.3.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -2015,12 +2194,12 @@
       }
     },
     "node_modules/@smithy/util-endpoints": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.7.tgz",
-      "integrity": "sha512-Q2gEind3jxoLk6hdKWyESMU7LnXz8aamVwM+VeVjOYzYT1PalGlY/ETa48hv2YpV4+YV604y93YngyzzzQ4IIA==",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.0.tgz",
+      "integrity": "sha512-gKzfdj5pyEOg1fVOsZVpVPRWAXbWqt9JgZdwU4cjKlJ57Fuccfk0ui5twh1TYvuJWtR2Tw3GwUmUuBM3qRWJJg==",
       "dependencies": {
-        "@smithy/node-config-provider": "^2.1.8",
-        "@smithy/types": "^2.7.0",
+        "@smithy/node-config-provider": "^2.2.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -2028,9 +2207,9 @@
       }
     },
     "node_modules/@smithy/util-hex-encoding": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz",
-      "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.0.tgz",
+      "integrity": "sha512-haxSIaBxn3p/lK+bEyqC32myHffacBLD61/HHzBGcG1Vo8dFTm5y0vhdR5R4wakW7H8Tr/czx+uckDOWZ1Km9Q==",
       "dependencies": {
         "tslib": "^2.5.0"
       },
@@ -2039,11 +2218,11 @@
       }
     },
     "node_modules/@smithy/util-middleware": {
-      "version": "2.0.8",
-      "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.8.tgz",
-      "integrity": "sha512-qkvqQjM8fRGGA8P2ydWylMhenCDP8VlkPn8kiNuFEaFz9xnUKC2irfqsBSJrfrOB9Qt6pQsI58r3zvvumhFMkw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.0.tgz",
+      "integrity": "sha512-bKfhAsdjRyGmYDsJUW5hPsL3qofgPgLPsuV+V6nNGyD/kjMobwstiIpA3ddGFT+XDwVOIUHElg7I06/wOpwKiQ==",
       "dependencies": {
-        "@smithy/types": "^2.7.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -2051,12 +2230,12 @@
       }
     },
     "node_modules/@smithy/util-retry": {
-      "version": "2.0.8",
-      "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.8.tgz",
-      "integrity": "sha512-cQTPnVaVFMjjS6cb44WV2yXtHVyXDC5icKyIbejMarJEApYeJWpBU3LINTxHqp/tyLI+MZOUdosr2mZ3sdziNg==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.0.tgz",
+      "integrity": "sha512-igJw+/olhAUtocMbEMBjy8SKRTHfefS+qcgmMUVEBLFgLjqMfpc8EDVB1BebNBQ1rre5yLDbi2UHUz48eZNkPQ==",
       "dependencies": {
-        "@smithy/service-error-classification": "^2.0.8",
-        "@smithy/types": "^2.7.0",
+        "@smithy/service-error-classification": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -2064,17 +2243,17 @@
       }
     },
     "node_modules/@smithy/util-stream": {
-      "version": "2.0.23",
-      "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.23.tgz",
-      "integrity": "sha512-OJMWq99LAZJUzUwTk+00plyxX3ESktBaGPhqNIEVab+53gLULiWN9B/8bRABLg0K6R6Xg4t80uRdhk3B/LZqMQ==",
-      "dependencies": {
-        "@smithy/fetch-http-handler": "^2.3.1",
-        "@smithy/node-http-handler": "^2.2.1",
-        "@smithy/types": "^2.7.0",
-        "@smithy/util-base64": "^2.0.1",
-        "@smithy/util-buffer-from": "^2.0.0",
-        "@smithy/util-hex-encoding": "^2.0.0",
-        "@smithy/util-utf8": "^2.0.2",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.0.tgz",
+      "integrity": "sha512-lcw9JVXLHvRawaXnfxdnGRw5pQM5c9XMEkBuMec+fIhGuPHIezqhQq7oO0jJcj0xwupJzW6HAvinktr9ozdKyg==",
+      "dependencies": {
+        "@smithy/fetch-http-handler": "^2.4.0",
+        "@smithy/node-http-handler": "^2.3.0",
+        "@smithy/types": "^2.9.0",
+        "@smithy/util-base64": "^2.1.0",
+        "@smithy/util-buffer-from": "^2.1.0",
+        "@smithy/util-hex-encoding": "^2.1.0",
+        "@smithy/util-utf8": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -2082,9 +2261,9 @@
       }
     },
     "node_modules/@smithy/util-uri-escape": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz",
-      "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.0.tgz",
+      "integrity": "sha512-ZHYFGyF9o/MHGMGtsHfkxnn2DhGRZlDIFGNgipu4K3x8jMEVahQ+tGnlkFVMM2QrSQHCcjICbBTJ5JEgaD5+Jg==",
       "dependencies": {
         "tslib": "^2.5.0"
       },
@@ -2093,11 +2272,11 @@
       }
     },
     "node_modules/@smithy/util-utf8": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz",
-      "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.0.tgz",
+      "integrity": "sha512-RnNNedYLpsNPQocMhr0nGEz0mGKdzI5dBi0h7vvmimULtBlyElgX1/hXozlkurIgx8R3bSy14/oRtmDsFClifg==",
       "dependencies": {
-        "@smithy/util-buffer-from": "^2.0.0",
+        "@smithy/util-buffer-from": "^2.1.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -2105,12 +2284,12 @@
       }
     },
     "node_modules/@smithy/util-waiter": {
-      "version": "2.0.15",
-      "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.0.15.tgz",
-      "integrity": "sha512-9Y+btzzB7MhLADW7xgD6SjvmoYaRkrb/9SCbNGmNdfO47v38rxb90IGXyDtAK0Shl9bMthTmLgjlfYc+vtz2Qw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.1.0.tgz",
+      "integrity": "sha512-BqfpYb4oNsQn6hhd4zDk8X6srVmiNOXHBFQz0vQSScS8Zliam7oLjlf/gHw02ewwxzi9229UQZF+UnG2jV6JGw==",
       "dependencies": {
-        "@smithy/abort-controller": "^2.0.15",
-        "@smithy/types": "^2.7.0",
+        "@smithy/abort-controller": "^2.1.0",
+        "@smithy/types": "^2.9.0",
         "tslib": "^2.5.0"
       },
       "engines": {
@@ -2141,18 +2320,42 @@
         "@types/trusted-types": "*"
       }
     },
+    "node_modules/@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
       "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
       "dev": true
     },
-    "node_modules/@types/node": {
-      "version": "16.18.68",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.68.tgz",
-      "integrity": "sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==",
+    "node_modules/@types/lodash": {
+      "version": "4.14.202",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
+      "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
       "dev": true
     },
+    "node_modules/@types/lodash.clonedeep": {
+      "version": "4.5.9",
+      "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz",
+      "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==",
+      "dev": true,
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "18.19.8",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.8.tgz",
+      "integrity": "sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg==",
+      "dev": true,
+      "dependencies": {
+        "undici-types": "~5.26.4"
+      }
+    },
     "node_modules/@types/semver": {
       "version": "7.5.6",
       "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
@@ -2360,15 +2563,15 @@
       }
     },
     "node_modules/@vitejs/plugin-vue": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
-      "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz",
+      "integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==",
       "dev": true,
       "engines": {
-        "node": "^14.18.0 || >=16.0.0"
+        "node": "^18.0.0 || >=20.0.0"
       },
       "peerDependencies": {
-        "vite": "^4.0.0",
+        "vite": "^5.0.0",
         "vue": "^3.2.25"
       }
     },
@@ -2401,12 +2604,13 @@
       }
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.10.tgz",
-      "integrity": "sha512-doe0hODR1+i1menPkRzJ5MNR6G+9uiZHIknK3Zn5OcIztu6GGw7u0XUzf3AgB8h/dfsZC9eouzoLo3c3+N/cVA==",
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz",
+      "integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==",
       "dependencies": {
-        "@babel/parser": "^7.23.5",
-        "@vue/shared": "3.3.10",
+        "@babel/parser": "^7.23.6",
+        "@vue/shared": "3.4.15",
+        "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
         "source-map-js": "^1.0.2"
       }
@@ -2417,28 +2621,27 @@
       "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.10.tgz",
-      "integrity": "sha512-NCrqF5fm10GXZIK0GrEAauBqdy+F2LZRt3yNHzrYjpYBuRssQbuPLtSnSNjyR9luHKkWSH8we5LMB3g+4z2HvA==",
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz",
+      "integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==",
       "dependencies": {
-        "@vue/compiler-core": "3.3.10",
-        "@vue/shared": "3.3.10"
+        "@vue/compiler-core": "3.4.15",
+        "@vue/shared": "3.4.15"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.10.tgz",
-      "integrity": "sha512-xpcTe7Rw7QefOTRFFTlcfzozccvjM40dT45JtrE3onGm/jBLZ0JhpKu3jkV7rbDFLeeagR/5RlJ2Y9SvyS0lAg==",
-      "dependencies": {
-        "@babel/parser": "^7.23.5",
-        "@vue/compiler-core": "3.3.10",
-        "@vue/compiler-dom": "3.3.10",
-        "@vue/compiler-ssr": "3.3.10",
-        "@vue/reactivity-transform": "3.3.10",
-        "@vue/shared": "3.3.10",
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz",
+      "integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==",
+      "dependencies": {
+        "@babel/parser": "^7.23.6",
+        "@vue/compiler-core": "3.4.15",
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/compiler-ssr": "3.4.15",
+        "@vue/shared": "3.4.15",
         "estree-walker": "^2.0.2",
         "magic-string": "^0.30.5",
-        "postcss": "^8.4.32",
+        "postcss": "^8.4.33",
         "source-map-js": "^1.0.2"
       }
     },
@@ -2459,12 +2662,12 @@
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.10.tgz",
-      "integrity": "sha512-12iM4jA4GEbskwXMmPcskK5wImc2ohKm408+o9iox3tfN9qua8xL0THIZtoe9OJHnXP4eOWZpgCAAThEveNlqQ==",
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz",
+      "integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==",
       "dependencies": {
-        "@vue/compiler-dom": "3.3.10",
-        "@vue/shared": "3.3.10"
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/shared": "3.4.15"
       }
     },
     "node_modules/@vue/devtools-api": {
@@ -2511,9 +2714,9 @@
       }
     },
     "node_modules/@vue/language-core": {
-      "version": "1.8.25",
-      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.25.tgz",
-      "integrity": "sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA==",
+      "version": "1.8.27",
+      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz",
+      "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==",
       "dev": true,
       "dependencies": {
         "@volar/language-core": "~1.11.1",
@@ -2560,87 +2763,59 @@
       }
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.10.tgz",
-      "integrity": "sha512-H5Z7rOY/JLO+e5a6/FEXaQ1TMuOvY4LDVgT+/+HKubEAgs9qeeZ+NhADSeEtrNQeiKLDuzeKc8v0CUFpB6Pqgw==",
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz",
+      "integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==",
       "dependencies": {
-        "@vue/shared": "3.3.10"
+        "@vue/shared": "3.4.15"
       }
     },
-    "node_modules/@vue/reactivity-transform": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.10.tgz",
-      "integrity": "sha512-0xBdk+CKHWT+Gev8oZ63Tc0qFfj935YZx+UAynlutnrDZ4diFCVFMWixn65HzjE3S1iJppWOo6Tt1OzASH7VEg==",
+    "node_modules/@vue/runtime-core": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz",
+      "integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==",
       "dependencies": {
-        "@babel/parser": "^7.23.5",
-        "@vue/compiler-core": "3.3.10",
-        "@vue/shared": "3.3.10",
-        "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.5"
+        "@vue/reactivity": "3.4.15",
+        "@vue/shared": "3.4.15"
       }
     },
-    "node_modules/@vue/reactivity-transform/node_modules/estree-walker": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
-      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz",
+      "integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==",
+      "dependencies": {
+        "@vue/runtime-core": "3.4.15",
+        "@vue/shared": "3.4.15",
+        "csstype": "^3.1.3"
+      }
     },
-    "node_modules/@vue/reactivity-transform/node_modules/magic-string": {
-      "version": "0.30.5",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
-      "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
+    "node_modules/@vue/server-renderer": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz",
+      "integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==",
       "dependencies": {
-        "@jridgewell/sourcemap-codec": "^1.4.15"
+        "@vue/compiler-ssr": "3.4.15",
+        "@vue/shared": "3.4.15"
       },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@vue/runtime-core": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.10.tgz",
-      "integrity": "sha512-DZ0v31oTN4YHX9JEU5VW1LoIVgFovWgIVb30bWn9DG9a7oA415idcwsRNNajqTx8HQJyOaWfRKoyuP2P2TYIag==",
-      "dependencies": {
-        "@vue/reactivity": "3.3.10",
-        "@vue/shared": "3.3.10"
-      }
-    },
-    "node_modules/@vue/runtime-dom": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.10.tgz",
-      "integrity": "sha512-c/jKb3ny05KJcYk0j1m7Wbhrxq7mZYr06GhKykDMNRRR9S+/dGT8KpHuNQjv3/8U4JshfkAk6TpecPD3B21Ijw==",
-      "dependencies": {
-        "@vue/runtime-core": "3.3.10",
-        "@vue/shared": "3.3.10",
-        "csstype": "^3.1.2"
-      }
-    },
-    "node_modules/@vue/server-renderer": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.10.tgz",
-      "integrity": "sha512-0i6ww3sBV3SKlF3YTjSVqKQ74xialMbjVYGy7cOTi7Imd8ediE7t72SK3qnvhrTAhOvlQhq6Bk6nFPdXxe0sAg==",
-      "dependencies": {
-        "@vue/compiler-ssr": "3.3.10",
-        "@vue/shared": "3.3.10"
-      },
-      "peerDependencies": {
-        "vue": "3.3.10"
+      "peerDependencies": {
+        "vue": "3.4.15"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
-      "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw=="
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
+      "integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g=="
     },
     "node_modules/@vue/tsconfig": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.4.0.tgz",
-      "integrity": "sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==",
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz",
+      "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
       "dev": true
     },
     "node_modules/acorn": {
-      "version": "8.11.2",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
-      "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
+      "version": "8.11.3",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
       "dev": true,
       "bin": {
         "acorn": "bin/acorn"
@@ -2778,12 +2953,12 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
-      "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
+      "version": "1.6.5",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
+      "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
       "dev": true,
       "dependencies": {
-        "follow-redirects": "^1.15.0",
+        "follow-redirects": "^1.15.4",
         "form-data": "^4.0.0",
         "proxy-from-env": "^1.1.0"
       }
@@ -2813,15 +2988,6 @@
         }
       ]
     },
-    "node_modules/big-integer": {
-      "version": "1.6.52",
-      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
-      "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.6"
-      }
-    },
     "node_modules/binary-extensions": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -2860,18 +3026,6 @@
       "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
       "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
     },
-    "node_modules/bplist-parser": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
-      "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
-      "dev": true,
-      "dependencies": {
-        "big-integer": "^1.6.44"
-      },
-      "engines": {
-        "node": ">= 5.10.0"
-      }
-    },
     "node_modules/brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2903,21 +3057,6 @@
         "ieee754": "^1.1.4"
       }
     },
-    "node_modules/bundle-name": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
-      "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
-      "dev": true,
-      "dependencies": {
-        "run-applescript": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/call-bind": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
@@ -2932,12 +3071,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/call-me-maybe": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
-      "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
-      "dev": true
-    },
     "node_modules/callsites": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2976,9 +3109,9 @@
       }
     },
     "node_modules/chart.js": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz",
-      "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==",
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz",
+      "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==",
       "dependencies": {
         "@kurkle/color": "^0.3.0"
       },
@@ -3152,40 +3285,6 @@
       "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
       "dev": true
     },
-    "node_modules/default-browser": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
-      "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==",
-      "dev": true,
-      "dependencies": {
-        "bundle-name": "^3.0.0",
-        "default-browser-id": "^3.0.0",
-        "execa": "^7.1.1",
-        "titleize": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=14.16"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/default-browser-id": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz",
-      "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==",
-      "dev": true,
-      "dependencies": {
-        "bplist-parser": "^0.2.0",
-        "untildify": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/define-data-property": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
@@ -3200,18 +3299,6 @@
         "node": ">= 0.4"
       }
     },
-    "node_modules/define-lazy-prop": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
-      "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/define-properties": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
@@ -3263,9 +3350,20 @@
       }
     },
     "node_modules/dompurify": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz",
-      "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w=="
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.8.tgz",
+      "integrity": "sha512-b7uwreMYL2eZhrSCRC4ahLTeZcPZxSmYfmcQGXGkXiZSNW1X85v+SDM5KsWcpivIiUBH47Ji7NtyUdpLeF5JZQ=="
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
     },
     "node_modules/error-ex": {
       "version": "1.3.2",
@@ -3361,12 +3459,11 @@
       }
     },
     "node_modules/esbuild": {
-      "version": "0.19.8",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.8.tgz",
-      "integrity": "sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==",
+      "version": "0.19.11",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz",
+      "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==",
       "dev": true,
       "hasInstallScript": true,
-      "peer": true,
       "bin": {
         "esbuild": "bin/esbuild"
       },
@@ -3374,28 +3471,29 @@
         "node": ">=12"
       },
       "optionalDependencies": {
-        "@esbuild/android-arm": "0.19.8",
-        "@esbuild/android-arm64": "0.19.8",
-        "@esbuild/android-x64": "0.19.8",
-        "@esbuild/darwin-arm64": "0.19.8",
-        "@esbuild/darwin-x64": "0.19.8",
-        "@esbuild/freebsd-arm64": "0.19.8",
-        "@esbuild/freebsd-x64": "0.19.8",
-        "@esbuild/linux-arm": "0.19.8",
-        "@esbuild/linux-arm64": "0.19.8",
-        "@esbuild/linux-ia32": "0.19.8",
-        "@esbuild/linux-loong64": "0.19.8",
-        "@esbuild/linux-mips64el": "0.19.8",
-        "@esbuild/linux-ppc64": "0.19.8",
-        "@esbuild/linux-riscv64": "0.19.8",
-        "@esbuild/linux-s390x": "0.19.8",
-        "@esbuild/linux-x64": "0.19.8",
-        "@esbuild/netbsd-x64": "0.19.8",
-        "@esbuild/openbsd-x64": "0.19.8",
-        "@esbuild/sunos-x64": "0.19.8",
-        "@esbuild/win32-arm64": "0.19.8",
-        "@esbuild/win32-ia32": "0.19.8",
-        "@esbuild/win32-x64": "0.19.8"
+        "@esbuild/aix-ppc64": "0.19.11",
+        "@esbuild/android-arm": "0.19.11",
+        "@esbuild/android-arm64": "0.19.11",
+        "@esbuild/android-x64": "0.19.11",
+        "@esbuild/darwin-arm64": "0.19.11",
+        "@esbuild/darwin-x64": "0.19.11",
+        "@esbuild/freebsd-arm64": "0.19.11",
+        "@esbuild/freebsd-x64": "0.19.11",
+        "@esbuild/linux-arm": "0.19.11",
+        "@esbuild/linux-arm64": "0.19.11",
+        "@esbuild/linux-ia32": "0.19.11",
+        "@esbuild/linux-loong64": "0.19.11",
+        "@esbuild/linux-mips64el": "0.19.11",
+        "@esbuild/linux-ppc64": "0.19.11",
+        "@esbuild/linux-riscv64": "0.19.11",
+        "@esbuild/linux-s390x": "0.19.11",
+        "@esbuild/linux-x64": "0.19.11",
+        "@esbuild/netbsd-x64": "0.19.11",
+        "@esbuild/openbsd-x64": "0.19.11",
+        "@esbuild/sunos-x64": "0.19.11",
+        "@esbuild/win32-arm64": "0.19.11",
+        "@esbuild/win32-ia32": "0.19.11",
+        "@esbuild/win32-x64": "0.19.11"
       }
     },
     "node_modules/escape-string-regexp": {
@@ -3477,23 +3575,24 @@
       }
     },
     "node_modules/eslint-plugin-prettier": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
-      "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==",
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
+      "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==",
       "dev": true,
       "dependencies": {
         "prettier-linter-helpers": "^1.0.0",
-        "synckit": "^0.8.5"
+        "synckit": "^0.8.6"
       },
       "engines": {
         "node": "^14.18.0 || >=16.0.0"
       },
       "funding": {
-        "url": "https://opencollective.com/prettier"
+        "url": "https://opencollective.com/eslint-plugin-prettier"
       },
       "peerDependencies": {
         "@types/eslint": ">=8.0.0",
         "eslint": ">=8.0.0",
+        "eslint-config-prettier": "*",
         "prettier": ">=3.0.0"
       },
       "peerDependenciesMeta": {
@@ -3506,9 +3605,9 @@
       }
     },
     "node_modules/eslint-plugin-vue": {
-      "version": "9.17.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.17.0.tgz",
-      "integrity": "sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==",
+      "version": "9.19.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.19.2.tgz",
+      "integrity": "sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
@@ -3689,29 +3788,6 @@
         "node": ">=0.8.x"
       }
     },
-    "node_modules/execa": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
-      "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
-      "dev": true,
-      "dependencies": {
-        "cross-spawn": "^7.0.3",
-        "get-stream": "^6.0.1",
-        "human-signals": "^4.3.0",
-        "is-stream": "^3.0.0",
-        "merge-stream": "^2.0.0",
-        "npm-run-path": "^5.1.0",
-        "onetime": "^6.0.0",
-        "signal-exit": "^3.0.7",
-        "strip-final-newline": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.18.0 || ^16.14.0 || >=18.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/execa?sponsor=1"
-      }
-    },
     "node_modules/fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3785,9 +3861,9 @@
       }
     },
     "node_modules/fastq": {
-      "version": "1.15.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
-      "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz",
+      "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==",
       "dev": true,
       "dependencies": {
         "reusify": "^1.0.4"
@@ -3862,9 +3938,9 @@
       "dev": true
     },
     "node_modules/follow-redirects": {
-      "version": "1.15.3",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
-      "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
+      "version": "1.15.5",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
+      "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
       "dev": true,
       "funding": [
         {
@@ -3989,18 +4065,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/get-stream": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
-      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/get-symbol-description": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -4050,9 +4114,9 @@
       }
     },
     "node_modules/globals": {
-      "version": "13.23.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
-      "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
+      "version": "13.24.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
       "dev": true,
       "dependencies": {
         "type-fest": "^0.20.2"
@@ -4242,21 +4306,21 @@
         "he": "bin/he"
       }
     },
+    "node_modules/highlight.js": {
+      "version": "11.9.0",
+      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz",
+      "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/hosted-git-info": {
       "version": "2.8.9",
       "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
       "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
       "dev": true
     },
-    "node_modules/human-signals": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
-      "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=14.18.0"
-      }
-    },
     "node_modules/idb-keyval": {
       "version": "6.2.1",
       "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz",
@@ -4449,21 +4513,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/is-docker": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
-      "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
-      "dev": true,
-      "bin": {
-        "is-docker": "cli.js"
-      },
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -4485,24 +4534,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/is-inside-container": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
-      "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
-      "dev": true,
-      "dependencies": {
-        "is-docker": "^3.0.0"
-      },
-      "bin": {
-        "is-inside-container": "cli.js"
-      },
-      "engines": {
-        "node": ">=14.16"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/is-negative-zero": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@@ -4576,18 +4607,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/is-stream": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
-      "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
-      "dev": true,
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/is-string": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@@ -4645,33 +4664,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/is-wsl": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
-      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
-      "dev": true,
-      "dependencies": {
-        "is-docker": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-wsl/node_modules/is-docker": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
-      "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
-      "dev": true,
-      "bin": {
-        "is-docker": "cli.js"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/isarray": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
@@ -4708,19 +4700,6 @@
       "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
       "dev": true
     },
-    "node_modules/json-schema-ref-parser": {
-      "version": "9.0.9",
-      "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz",
-      "integrity": "sha512-qcP2lmGy+JUoQJ4DOQeLaZDqH9qSkeGCK3suKWxJXS82dg728Mn3j97azDMaOUmJAN4uCq91LdPx4K7E8F1a7Q==",
-      "deprecated": "Please switch to @apidevtools/json-schema-ref-parser",
-      "dev": true,
-      "dependencies": {
-        "@apidevtools/json-schema-ref-parser": "9.0.9"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/json-schema-traverse": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
@@ -4802,6 +4781,12 @@
       "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
       "dev": true
     },
+    "node_modules/lodash.clonedeep": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+      "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+      "dev": true
+    },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -4837,12 +4822,6 @@
         "node": ">= 0.10.0"
       }
     },
-    "node_modules/merge-stream": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
-      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
-      "dev": true
-    },
     "node_modules/merge2": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -4886,18 +4865,6 @@
         "node": ">= 0.6"
       }
     },
-    "node_modules/mimic-fn": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
-      "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -5165,33 +5132,6 @@
         "which": "bin/which"
       }
     },
-    "node_modules/npm-run-path": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
-      "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
-      "dev": true,
-      "dependencies": {
-        "path-key": "^4.0.0"
-      },
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/npm-run-path/node_modules/path-key": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
-      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/nth-check": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -5249,50 +5189,17 @@
         "wrappy": "1"
       }
     },
-    "node_modules/onetime": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
-      "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
-      "dev": true,
-      "dependencies": {
-        "mimic-fn": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/open": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
-      "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
-      "dev": true,
-      "dependencies": {
-        "default-browser": "^4.0.0",
-        "define-lazy-prop": "^3.0.0",
-        "is-inside-container": "^1.0.0",
-        "is-wsl": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=14.16"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/openapi-typescript-codegen": {
-      "version": "0.25.0",
-      "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.25.0.tgz",
-      "integrity": "sha512-nN/TnIcGbP58qYgwEEy5FrAAjePcYgfMaCe3tsmYyTgI3v4RR9v8os14L+LEWDvV50+CmqiyTzRkKKtJeb6Ybg==",
+      "version": "0.26.0",
+      "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.26.0.tgz",
+      "integrity": "sha512-5D45xawBHu9efxaDdioeZAfAQGxE0v12aNk24dTymNHRA75A6bFP8tKUAIzx8+AKWN7d/S9RWD87yVWhlFLhDA==",
       "dev": true,
       "dependencies": {
+        "@apidevtools/json-schema-ref-parser": "^11.1.0",
         "camelcase": "^6.3.0",
-        "commander": "^11.0.0",
-        "fs-extra": "^11.1.1",
-        "handlebars": "^4.7.7",
-        "json-schema-ref-parser": "^9.0.9"
+        "commander": "^11.1.0",
+        "fs-extra": "^11.2.0",
+        "handlebars": "^4.7.8"
       },
       "bin": {
         "openapi": "bin/index.js"
@@ -5507,9 +5414,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.32",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
-      "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
+      "version": "8.4.33",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
+      "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
       "funding": [
         {
           "type": "opencollective",
@@ -5534,9 +5441,9 @@
       }
     },
     "node_modules/postcss-selector-parser": {
-      "version": "6.0.13",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
-      "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
+      "version": "6.0.15",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
+      "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
       "dev": true,
       "dependencies": {
         "cssesc": "^3.0.0",
@@ -5744,18 +5651,34 @@
       }
     },
     "node_modules/rollup": {
-      "version": "3.29.4",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
-      "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
+      "version": "4.9.5",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.5.tgz",
+      "integrity": "sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==",
       "dev": true,
+      "dependencies": {
+        "@types/estree": "1.0.5"
+      },
       "bin": {
         "rollup": "dist/bin/rollup"
       },
       "engines": {
-        "node": ">=14.18.0",
+        "node": ">=18.0.0",
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.9.5",
+        "@rollup/rollup-android-arm64": "4.9.5",
+        "@rollup/rollup-darwin-arm64": "4.9.5",
+        "@rollup/rollup-darwin-x64": "4.9.5",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.9.5",
+        "@rollup/rollup-linux-arm64-gnu": "4.9.5",
+        "@rollup/rollup-linux-arm64-musl": "4.9.5",
+        "@rollup/rollup-linux-riscv64-gnu": "4.9.5",
+        "@rollup/rollup-linux-x64-gnu": "4.9.5",
+        "@rollup/rollup-linux-x64-musl": "4.9.5",
+        "@rollup/rollup-win32-arm64-msvc": "4.9.5",
+        "@rollup/rollup-win32-ia32-msvc": "4.9.5",
+        "@rollup/rollup-win32-x64-msvc": "4.9.5",
         "fsevents": "~2.3.2"
       }
     },
@@ -5789,149 +5712,45 @@
         "estree-walker": "^0.6.1"
       }
     },
-    "node_modules/run-applescript": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
-      "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
       "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
       "dependencies": {
-        "execa": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "queue-microtask": "^1.2.2"
       }
     },
-    "node_modules/run-applescript/node_modules/execa": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
-      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+    "node_modules/safe-array-concat": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz",
+      "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==",
       "dev": true,
       "dependencies": {
-        "cross-spawn": "^7.0.3",
-        "get-stream": "^6.0.0",
-        "human-signals": "^2.1.0",
-        "is-stream": "^2.0.0",
-        "merge-stream": "^2.0.0",
-        "npm-run-path": "^4.0.1",
-        "onetime": "^5.1.2",
-        "signal-exit": "^3.0.3",
-        "strip-final-newline": "^2.0.0"
+        "call-bind": "^1.0.5",
+        "get-intrinsic": "^1.2.2",
+        "has-symbols": "^1.0.3",
+        "isarray": "^2.0.5"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=0.4"
       },
       "funding": {
-        "url": "https://github.com/sindresorhus/execa?sponsor=1"
-      }
-    },
-    "node_modules/run-applescript/node_modules/human-signals": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
-      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
-      "dev": true,
-      "engines": {
-        "node": ">=10.17.0"
-      }
-    },
-    "node_modules/run-applescript/node_modules/is-stream": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/run-applescript/node_modules/mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/run-applescript/node_modules/npm-run-path": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
-      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
-      "dev": true,
-      "dependencies": {
-        "path-key": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/run-applescript/node_modules/onetime": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
-      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
-      "dev": true,
-      "dependencies": {
-        "mimic-fn": "^2.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/run-applescript/node_modules/strip-final-newline": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
-      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/run-parallel": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "dependencies": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "node_modules/safe-array-concat": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
-      "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.1",
-        "has-symbols": "^1.0.3",
-        "isarray": "^2.0.5"
-      },
-      "engines": {
-        "node": ">=0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
     "node_modules/safe-buffer": {
@@ -5954,15 +5773,18 @@
       ]
     },
     "node_modules/safe-regex-test": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
-      "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz",
+      "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.1.3",
+        "call-bind": "^1.0.5",
+        "get-intrinsic": "^1.2.2",
         "is-regex": "^1.1.4"
       },
+      "engines": {
+        "node": ">= 0.4"
+      },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -5999,15 +5821,16 @@
       }
     },
     "node_modules/set-function-length": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
-      "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz",
+      "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==",
       "dev": true,
       "dependencies": {
         "define-data-property": "^1.1.1",
-        "get-intrinsic": "^1.2.1",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.2",
         "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.0"
+        "has-property-descriptors": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -6094,12 +5917,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/signal-exit": {
-      "version": "3.0.7",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-      "dev": true
-    },
     "node_modules/slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -6265,18 +6082,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/strip-final-newline": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
-      "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -6319,12 +6124,12 @@
       }
     },
     "node_modules/synckit": {
-      "version": "0.8.6",
-      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.6.tgz",
-      "integrity": "sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==",
+      "version": "0.8.8",
+      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz",
+      "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==",
       "dev": true,
       "dependencies": {
-        "@pkgr/utils": "^2.4.2",
+        "@pkgr/core": "^0.1.0",
         "tslib": "^2.6.2"
       },
       "engines": {
@@ -6340,18 +6145,6 @@
       "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
       "dev": true
     },
-    "node_modules/titleize": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
-      "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -6520,6 +6313,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "dev": true
+    },
     "node_modules/universalify": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@@ -6529,15 +6328,6 @@
         "node": ">= 10.0.0"
       }
     },
-    "node_modules/untildify": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
-      "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/uri-js": {
       "version": "4.4.1",
       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -6570,29 +6360,29 @@
       }
     },
     "node_modules/vite": {
-      "version": "4.4.12",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.12.tgz",
-      "integrity": "sha512-KtPlUbWfxzGVul8Nut8Gw2Qe8sBzWY+8QVc5SL8iRFnpnrcoCaNlzO40c1R6hPmcdTwIPEDkq0Y9+27a5tVbdQ==",
+      "version": "5.0.11",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz",
+      "integrity": "sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==",
       "dev": true,
       "dependencies": {
-        "esbuild": "^0.18.10",
-        "postcss": "^8.4.27",
-        "rollup": "^3.27.1"
+        "esbuild": "^0.19.3",
+        "postcss": "^8.4.32",
+        "rollup": "^4.2.0"
       },
       "bin": {
         "vite": "bin/vite.js"
       },
       "engines": {
-        "node": "^14.18.0 || >=16.0.0"
+        "node": "^18.0.0 || >=20.0.0"
       },
       "funding": {
         "url": "https://github.com/vitejs/vite?sponsor=1"
       },
       "optionalDependencies": {
-        "fsevents": "~2.3.2"
+        "fsevents": "~2.3.3"
       },
       "peerDependencies": {
-        "@types/node": ">= 14",
+        "@types/node": "^18.0.0 || >=20.0.0",
         "less": "*",
         "lightningcss": "^1.21.0",
         "sass": "*",
@@ -6624,405 +6414,16 @@
         }
       }
     },
-    "node_modules/vite/node_modules/@esbuild/android-arm": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
-      "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/android-arm64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
-      "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/android-x64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
-      "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
-      "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/darwin-x64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
-      "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
-      "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
-      "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-arm": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
-      "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-arm64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
-      "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-ia32": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
-      "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
-      "cpu": [
-        "ia32"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-loong64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
-      "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
-      "cpu": [
-        "loong64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
-      "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
-      "cpu": [
-        "mips64el"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
-      "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
-      "cpu": [
-        "ppc64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
-      "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
-      "cpu": [
-        "riscv64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-s390x": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
-      "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
-      "cpu": [
-        "s390x"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-x64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
-      "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
-      "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "netbsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
-      "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "openbsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/sunos-x64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
-      "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "sunos"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-arm64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
-      "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-ia32": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
-      "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
-      "cpu": [
-        "ia32"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-x64": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
-      "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/esbuild": {
-      "version": "0.18.20",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
-      "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
-      "dev": true,
-      "hasInstallScript": true,
-      "bin": {
-        "esbuild": "bin/esbuild"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "optionalDependencies": {
-        "@esbuild/android-arm": "0.18.20",
-        "@esbuild/android-arm64": "0.18.20",
-        "@esbuild/android-x64": "0.18.20",
-        "@esbuild/darwin-arm64": "0.18.20",
-        "@esbuild/darwin-x64": "0.18.20",
-        "@esbuild/freebsd-arm64": "0.18.20",
-        "@esbuild/freebsd-x64": "0.18.20",
-        "@esbuild/linux-arm": "0.18.20",
-        "@esbuild/linux-arm64": "0.18.20",
-        "@esbuild/linux-ia32": "0.18.20",
-        "@esbuild/linux-loong64": "0.18.20",
-        "@esbuild/linux-mips64el": "0.18.20",
-        "@esbuild/linux-ppc64": "0.18.20",
-        "@esbuild/linux-riscv64": "0.18.20",
-        "@esbuild/linux-s390x": "0.18.20",
-        "@esbuild/linux-x64": "0.18.20",
-        "@esbuild/netbsd-x64": "0.18.20",
-        "@esbuild/openbsd-x64": "0.18.20",
-        "@esbuild/sunos-x64": "0.18.20",
-        "@esbuild/win32-arm64": "0.18.20",
-        "@esbuild/win32-ia32": "0.18.20",
-        "@esbuild/win32-x64": "0.18.20"
-      }
-    },
     "node_modules/vue": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.10.tgz",
-      "integrity": "sha512-zg6SIXZdTBwiqCw/1p+m04VyHjLfwtjwz8N57sPaBhEex31ND0RYECVOC1YrRwMRmxFf5T1dabl6SGUbMKKuVw==",
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz",
+      "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==",
       "dependencies": {
-        "@vue/compiler-dom": "3.3.10",
-        "@vue/compiler-sfc": "3.3.10",
-        "@vue/runtime-dom": "3.3.10",
-        "@vue/server-renderer": "3.3.10",
-        "@vue/shared": "3.3.10"
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/compiler-sfc": "3.4.15",
+        "@vue/runtime-dom": "3.4.15",
+        "@vue/server-renderer": "3.4.15",
+        "@vue/shared": "3.4.15"
       },
       "peerDependencies": {
         "typescript": "*"
@@ -7034,9 +6435,9 @@
       }
     },
     "node_modules/vue-eslint-parser": {
-      "version": "9.3.2",
-      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
-      "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==",
+      "version": "9.4.0",
+      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.0.tgz",
+      "integrity": "sha512-7KsNBb6gHFA75BtneJsoK/dbZ281whUIwFYdQxA68QrCrGMXYzUMbPDHGcOQ0OocIVKrWSKWXZ4mL7tonCXoUw==",
       "dev": true,
       "dependencies": {
         "debug": "^4.3.4",
@@ -7097,9 +6498,9 @@
       }
     },
     "node_modules/vue-template-compiler": {
-      "version": "2.7.15",
-      "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.15.tgz",
-      "integrity": "sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==",
+      "version": "2.7.16",
+      "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
+      "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==",
       "dev": true,
       "dependencies": {
         "de-indent": "^1.0.2",
@@ -7107,13 +6508,13 @@
       }
     },
     "node_modules/vue-tsc": {
-      "version": "1.8.25",
-      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.25.tgz",
-      "integrity": "sha512-lHsRhDc/Y7LINvYhZ3pv4elflFADoEOo67vfClAfF2heVHpHmVquLSjojgCSIwzA4F0Pc4vowT/psXCYcfk+iQ==",
+      "version": "1.8.27",
+      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz",
+      "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==",
       "dev": true,
       "dependencies": {
         "@volar/typescript": "~1.11.1",
-        "@vue/language-core": "1.8.25",
+        "@vue/language-core": "1.8.27",
         "semver": "^7.5.4"
       },
       "bin": {
diff --git a/package.json b/package.json
index 2c7bbd8337e0cd7a9423ddcdac50b5c2d64a212e..f1334915fbe36a246e49a509047a78ec69bb156a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,8 @@
 {
   "name": "clowm-ui",
   "version": "2.0.0",
+  "type": "module",
+  "license": "Apache-2.0",
   "scripts": {
     "dev": "vite",
     "build": "run-p type-check build-only",
@@ -10,7 +12,8 @@
     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
     "generate-s3-client": "openapi --input http://localhost:9999/api/s3proxy-service/openapi.json --output src/client/s3proxy --client axios",
     "generate-auth-client": "openapi --input http://localhost:9999/api/auth-service/openapi.json --output src/client/auth --client axios",
-    "generate-workflow-client": "openapi --input http://localhost:9999/api/workflow-service/openapi.json --output src/client/workflow --client axios"
+    "generate-workflow-client": "openapi --input http://localhost:9999/api/workflow-service/openapi.json --output src/client/workflow --client axios",
+    "generate-resource-client": "openapi --input http://localhost:9999/api/resource-service/openapi.json --output src/client/resource --client axios"
   },
   "dependencies": {
     "@aws-sdk/client-s3": "^3.440.0",
@@ -19,44 +22,45 @@
     "@fortawesome/fontawesome-free": "~6.4.2",
     "@popperjs/core": "~2.11.8",
     "ajv": "~8.12.0",
-    "bootstrap": "~5.3.1",
-    "chart.js": "~4.3.3",
+    "bootstrap": "~5.3.0",
+    "chart.js": "~4.4.0",
     "chartjs-plugin-zoom": "~2.0.1",
-    "dayjs": "~1.11.9",
+    "dayjs": "~1.11.0",
     "dompurify": "~3.0.5",
     "filesize": "~10.0.12",
     "idb-keyval": "^6.2.1",
-    "pinia": "~2.1.6",
-    "semver": "~7.5.4",
+    "pinia": "~2.1.0",
+    "semver": "~7.5.0",
     "showdown": "~2.1.0",
-    "vue": "~3.3.4",
-    "vue-router": "~4.2.4",
-    "vue3-cookies": "~1.0.6"
+    "vue": "~3.4.0",
+    "vue-router": "~4.2.0",
+    "vue3-cookies": "~1.0.0"
   },
   "devDependencies": {
     "@esbuild-plugins/node-globals-polyfill": "~0.2.3",
     "@esbuild-plugins/node-modules-polyfill": "~0.2.2",
     "@rushstack/eslint-patch": "~1.2.0",
     "@tsconfig/node18": "^18.2.1",
-    "@types/bootstrap": "~5.2.6",
-    "@types/dompurify": "~3.0.2",
-    "@types/node": "^16.18.48",
+    "@types/bootstrap": "~5.2.0",
+    "@types/dompurify": "~3.0.0",
+    "@types/node": "^18.19.5",
     "@types/semver": "~7.5.1",
     "@types/showdown": "~2.0.1",
-    "@vitejs/plugin-vue": "~4.3.4",
+    "@vitejs/plugin-vue": "~5.0.0",
     "@vue/eslint-config-prettier": "~8.0.0",
     "@vue/eslint-config-typescript": "~11.0.3",
-    "@vue/tsconfig": "~0.4.0",
+    "@vue/tsconfig": "~0.5.0",
     "axios": "~1.6.0",
     "eslint": "~8.48.0",
-    "eslint-plugin-vue": "~9.17.0",
+    "eslint-plugin-vue": "~9.19.0",
+    "highlight.js": "^11.9.0",
     "npm-run-all": "~4.1.5",
-    "openapi-typescript-codegen": "^0.25.0",
+    "openapi-typescript-codegen": "^0.26.0",
     "prettier": "~3.0.3",
     "rollup-plugin-node-polyfills": "~0.2.1",
     "sass": "~1.66.1",
     "typescript": "~5.1.6",
-    "vite": "~4.4.9",
-    "vue-tsc": "~1.8.10"
+    "vite": "~5.0.0",
+    "vue-tsc": "~1.8.0"
   }
 }
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000000000000000000000000000000000000..425b9009cc803d48afe473fd660965af121b1f73
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,3 @@
+User-agent: *
+Disallow: /dashboard
+Allow: /login
diff --git a/src/App.vue b/src/App.vue
index a9b94a9a34d38fffe98a9e950256d42049652a10..65c7b0dfe57e1c361aa9ea7d36cad82395b59ca6 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -7,18 +7,22 @@ import { useRouter } from "vue-router";
 import { OpenAPI as S3ProxyOpenAPI } from "@/client/s3proxy";
 import { OpenAPI as AuthOpenAPI } from "@/client/auth";
 import { OpenAPI as WorkflowOpenAPI } from "@/client/workflow";
+import { OpenAPI as ResourceOpenAPI } from "@/client/resource";
 import { environment } from "@/environment";
 import FooterBottom from "@/components/FooterBottom.vue";
 import axios from "axios";
+import { useNameStore } from "@/stores/names";
 
 const { cookies } = useCookies();
 const store = useAuthStore();
 const router = useRouter();
+const nameRepository = useNameStore();
 
 onBeforeMount(() => {
   S3ProxyOpenAPI.BASE = environment.S3PROXY_API_BASE_URL;
   AuthOpenAPI.BASE = environment.AUTH_API_BASE_URL;
   WorkflowOpenAPI.BASE = environment.WORKFLOW_API_BASE_URL;
+  ResourceOpenAPI.BASE = environment.RESOURCE_API_BASE_URL;
   axios.interceptors.response.use(
     (res) => res,
     (err) => {
@@ -56,20 +60,36 @@ onBeforeMount(() => {
       return { name: "dashboard" };
     } else if (
       to.meta.requiresReviewerRole &&
-      !(store.workflowReviewer || store.admin)
+      !(store.rewiewer || store.admin)
     ) {
       return { name: "dashboard" };
+    } else if (
+      to.meta.requiresMaintainerRole &&
+      !(store.resourceMaintainer || store.admin)
+    ) {
+      return { name: "dashboard" };
+    } else if (to.meta.adminRole && !store.admin) {
+      return { name: "dashboard" };
     }
   });
+  nameRepository.loadNameMapping();
 });
 </script>
 
 <template>
   <NavbarTop />
   <div class="container-xxl mt-4 flex-grow-1">
+    <div
+      id="global-toast-container"
+      class="toast-container position-fixed top-toast end-0 p-3"
+    ></div>
     <router-view></router-view>
   </div>
   <FooterBottom />
 </template>
 
-<style scoped></style>
+<style scoped>
+.top-toast {
+  top: 4rem;
+}
+</style>
diff --git a/src/assets/env.template.js b/src/assets/env.template.js
index c2ddbd6f476a9e787b158e38a695bef8924f79c9..5667de5dae989b17408ae49369cc2069c234718a 100644
--- a/src/assets/env.template.js
+++ b/src/assets/env.template.js
@@ -6,5 +6,6 @@
   window["env"]["workflowApiUrl"] = "${WORKFLOW_API_BASE_URL}";
   window["env"]["s3proxyApiUrl"] = "${S3PROXY_API_BASE_URL}";
   window["env"]["authApiUrl"] = "${AUTH_API_BASE_URL}";
+  window["env"]["resourceApiUrl"] = "${RESOURCE_API_BASE_URL}";
   window["env"]["devSystem"] = "${DEV_SYSTEM}";
 })(this);
diff --git a/src/assets/main.css b/src/assets/main.css
index 24f661e1ba5b45e145150f636947b284828c9656..cb6974cba0e2a5c6b64ede79fec9c1befe81c2f9 100644
--- a/src/assets/main.css
+++ b/src/assets/main.css
@@ -1,13 +1,10 @@
 @import "base.css";
+@import "highlight.js/styles/default.min.css";
 
 .fs-0 {
     font-size: 3.5rem;
 }
 
-.top-toast {
-    top: 4rem;
-}
-
 .w-fit {
     width: fit-content;
 }
@@ -32,3 +29,7 @@ pre {
     transform: translateY(-2px);
     backdrop-filter: brightness(0.95);
 }
+
+.hover-info:hover {
+    color: var(--bs-info) !important;
+}
diff --git a/src/client/auth/core/CancelablePromise.ts b/src/client/auth/core/CancelablePromise.ts
index 55fef8517238d2dab7478598eefca72e657b9fb5..eb02246c31f61b897667a6433d55b8b413bafbc1 100644
--- a/src/client/auth/core/CancelablePromise.ts
+++ b/src/client/auth/core/CancelablePromise.ts
@@ -51,7 +51,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isResolved = true;
-                this.#resolve?.(value);
+                if (this.#resolve) this.#resolve(value);
             };
 
             const onReject = (reason?: any): void => {
@@ -59,7 +59,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isRejected = true;
-                this.#reject?.(reason);
+                if (this.#reject) this.#reject(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
@@ -122,7 +122,7 @@ export class CancelablePromise<T> implements Promise<T> {
             }
         }
         this.#cancelHandlers.length = 0;
-        this.#reject?.(new CancelError('Request aborted'));
+        if (this.#reject) this.#reject(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
diff --git a/src/client/auth/core/request.ts b/src/client/auth/core/request.ts
index 1142d43219797c94e09f35ba83a9f5441892ddd2..c6a0602a006d2d1855c2ab50d2d7f43c2eb1bab4 100644
--- a/src/client/auth/core/request.ts
+++ b/src/client/auth/core/request.ts
@@ -145,10 +145,13 @@ export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Reso
 };
 
 export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
-    const token = await resolve(options, config.TOKEN);
-    const username = await resolve(options, config.USERNAME);
-    const password = await resolve(options, config.PASSWORD);
-    const additionalHeaders = await resolve(options, config.HEADERS);
+    const [token, username, password, additionalHeaders] = await Promise.all([
+        resolve(options, config.TOKEN),
+        resolve(options, config.USERNAME),
+        resolve(options, config.PASSWORD),
+        resolve(options, config.HEADERS),
+    ]);
+
     const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
 
     const headers = Object.entries({
diff --git a/src/client/auth/models/ErrorDetail.ts b/src/client/auth/models/ErrorDetail.ts
index eba50ab9335b70df9b29cada4c54cf9ffaad10a4..69dd37eb86bc15e2ab2037f3f82516493801e74d 100644
--- a/src/client/auth/models/ErrorDetail.ts
+++ b/src/client/auth/models/ErrorDetail.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a error due to a rejected request.
  */
diff --git a/src/client/auth/models/HTTPValidationError.ts b/src/client/auth/models/HTTPValidationError.ts
index c0bcc87cf7f3222638466bc2e5753ca1accf01f7..892e4257ccbb49769737cb2e78e0aad49d81a3b1 100644
--- a/src/client/auth/models/HTTPValidationError.ts
+++ b/src/client/auth/models/HTTPValidationError.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { ValidationError } from './ValidationError';
-
 export type HTTPValidationError = {
     detail?: Array<ValidationError>;
 };
diff --git a/src/client/auth/models/RoleEnum.ts b/src/client/auth/models/RoleEnum.ts
index c38121aa528efe32ad2db1868fe05ca559006f4b..d1a23ae7bd28accb81fa3d196c78cafd7aebe846 100644
--- a/src/client/auth/models/RoleEnum.ts
+++ b/src/client/auth/models/RoleEnum.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the Roles in the CloWM Services.
  */
@@ -12,4 +11,5 @@ export enum RoleEnum {
     REVIEWER = 'reviewer',
     DEVELOPER = 'developer',
     FOREIGN_USER = 'foreign_user',
+    DB_MAINTAINER = 'db_maintainer',
 }
diff --git a/src/client/auth/models/User.ts b/src/client/auth/models/User.ts
index 8804790e499d6f84af8e66835bdfc8f9a1a269ff..5a1a2a0818d6fde1ed83cf5085a558edd232589b 100644
--- a/src/client/auth/models/User.ts
+++ b/src/client/auth/models/User.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { RoleEnum } from './RoleEnum';
-
 /**
  * Schema for a user.
  */
diff --git a/src/client/auth/models/ValidationError.ts b/src/client/auth/models/ValidationError.ts
index 18997ec72f4103731f38d915508522ba23ba8506..f2ff49a2b9ad95d18a90c95dc2a7c0b60b7e85e7 100644
--- a/src/client/auth/models/ValidationError.ts
+++ b/src/client/auth/models/ValidationError.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ValidationError = {
     loc: Array<(string | number)>;
     msg: string;
diff --git a/src/client/auth/services/AuthService.ts b/src/client/auth/services/AuthService.ts
index 0472d3962a5f73dac257fe6de7e4c74cfe56f987..ff4c0213646a9f24e8e8471bcd4cae3f116af2b7 100644
--- a/src/client/auth/services/AuthService.ts
+++ b/src/client/auth/services/AuthService.ts
@@ -5,9 +5,7 @@
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class AuthService {
-
     /**
      * Redirect to LifeScience OIDC Login
      * Redirect route to OIDC provider to kickstart the login process.
@@ -23,7 +21,6 @@ export class AuthService {
             },
         });
     }
-
     /**
      * LifeScience Login Callback
      * Callback for the Life Science Identity Provider.
@@ -48,5 +45,4 @@ export class AuthService {
             },
         });
     }
-
 }
diff --git a/src/client/auth/services/UserService.ts b/src/client/auth/services/UserService.ts
index ae578426600f01927ec075bafe3d68b849366327..0e52ed2f45be911672273c012f8c53859ca25c4c 100644
--- a/src/client/auth/services/UserService.ts
+++ b/src/client/auth/services/UserService.ts
@@ -4,17 +4,14 @@
 /* eslint-disable */
 import type { RoleEnum } from '../models/RoleEnum';
 import type { User } from '../models/User';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class UserService {
-
     /**
      * Get the logged in user
      * Return the user associated with the used JWT.
-     * Permission 'user:read' required.
+     * Permission `user:read` required.
      * @returns User Successful Response
      * @throws ApiError
      */
@@ -29,21 +26,21 @@ export class UserService {
             },
         });
     }
-
     /**
      * List users and search by their name
      * Return the users that have a specific substring in their name.
-     * Permission 'user:read_any' required, except when 'name_substring' as only query parameter is set.
-     * Then permission 'user:search' required.
-     * @param nameSubstring Filter users by a substring in their name. Permission 'search' required
-     * @param filterRoles Filter users by their role. If multiple are selected, they are concatenating by an OR Expresssion. Permission 'read_any' required
-     * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission 'read_any' required.
+     *
+     * Permission `user:read_any` required, except when `name_substring` as only query parameter is set,
+     * then permission `user:search` required.
+     * @param nameSubstring Filter users by a substring in their name. Permission `user:search` required
+     * @param filterRoles Filter users by their role. If multiple are selected, they are concatenating by an OR Expression. Permission `user:read_any` required
+     * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission `user:read_any` required.
      * @returns User Successful Response
      * @throws ApiError
      */
     public static userListUsers(
-        nameSubstring?: (string | null),
-        filterRoles?: (Array<RoleEnum> | null),
+        nameSubstring?: string,
+        filterRoles?: Array<RoleEnum>,
         includeRoles: boolean = false,
     ): CancelablePromise<Array<User>> {
         return __request(OpenAPI, {
@@ -62,13 +59,13 @@ export class UserService {
             },
         });
     }
-
     /**
      * Get a user by its uid
-     * Return the user with the specific uid. A user can only view himself.
-     * Permission 'user:read' required
+     * Return the user with the specific uid.
+     *
+     * Permission `user:read` required if the current user has the same uid as `uid` otherwise `user:read_any` required.
      * @param uid UID of a user
-     * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission 'read_any' required.
+     * @param includeRoles Flag whether to include the roles of the users in the response. If True, permission `user:read_any` required.
      * @returns User Successful Response
      * @throws ApiError
      */
@@ -93,5 +90,4 @@ export class UserService {
             },
         });
     }
-
 }
diff --git a/src/client/resource/core/ApiError.ts b/src/client/resource/core/ApiError.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d6b8fcc3ad0b6b2bdf1aa4df97ec598e64995648
--- /dev/null
+++ b/src/client/resource/core/ApiError.ts
@@ -0,0 +1,25 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { ApiResult } from './ApiResult';
+
+export class ApiError extends Error {
+    public readonly url: string;
+    public readonly status: number;
+    public readonly statusText: string;
+    public readonly body: any;
+    public readonly request: ApiRequestOptions;
+
+    constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
+        super(message);
+
+        this.name = 'ApiError';
+        this.url = response.url;
+        this.status = response.status;
+        this.statusText = response.statusText;
+        this.body = response.body;
+        this.request = request;
+    }
+}
diff --git a/src/client/resource/core/ApiRequestOptions.ts b/src/client/resource/core/ApiRequestOptions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c19adcc94dc5f015865368d6d64751de5c243aa3
--- /dev/null
+++ b/src/client/resource/core/ApiRequestOptions.ts
@@ -0,0 +1,17 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ApiRequestOptions = {
+    readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
+    readonly url: string;
+    readonly path?: Record<string, any>;
+    readonly cookies?: Record<string, any>;
+    readonly headers?: Record<string, any>;
+    readonly query?: Record<string, any>;
+    readonly formData?: Record<string, any>;
+    readonly body?: any;
+    readonly mediaType?: string;
+    readonly responseHeader?: string;
+    readonly errors?: Record<number, string>;
+};
diff --git a/src/client/resource/core/ApiResult.ts b/src/client/resource/core/ApiResult.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ad8fef2bc334c2aedf5c043896fd394229453ff0
--- /dev/null
+++ b/src/client/resource/core/ApiResult.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ApiResult = {
+    readonly url: string;
+    readonly ok: boolean;
+    readonly status: number;
+    readonly statusText: string;
+    readonly body: any;
+};
diff --git a/src/client/resource/core/CancelablePromise.ts b/src/client/resource/core/CancelablePromise.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb02246c31f61b897667a6433d55b8b413bafbc1
--- /dev/null
+++ b/src/client/resource/core/CancelablePromise.ts
@@ -0,0 +1,131 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export class CancelError extends Error {
+
+    constructor(message: string) {
+        super(message);
+        this.name = 'CancelError';
+    }
+
+    public get isCancelled(): boolean {
+        return true;
+    }
+}
+
+export interface OnCancel {
+    readonly isResolved: boolean;
+    readonly isRejected: boolean;
+    readonly isCancelled: boolean;
+
+    (cancelHandler: () => void): void;
+}
+
+export class CancelablePromise<T> implements Promise<T> {
+    #isResolved: boolean;
+    #isRejected: boolean;
+    #isCancelled: boolean;
+    readonly #cancelHandlers: (() => void)[];
+    readonly #promise: Promise<T>;
+    #resolve?: (value: T | PromiseLike<T>) => void;
+    #reject?: (reason?: any) => void;
+
+    constructor(
+        executor: (
+            resolve: (value: T | PromiseLike<T>) => void,
+            reject: (reason?: any) => void,
+            onCancel: OnCancel
+        ) => void
+    ) {
+        this.#isResolved = false;
+        this.#isRejected = false;
+        this.#isCancelled = false;
+        this.#cancelHandlers = [];
+        this.#promise = new Promise<T>((resolve, reject) => {
+            this.#resolve = resolve;
+            this.#reject = reject;
+
+            const onResolve = (value: T | PromiseLike<T>): void => {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+                    return;
+                }
+                this.#isResolved = true;
+                if (this.#resolve) this.#resolve(value);
+            };
+
+            const onReject = (reason?: any): void => {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+                    return;
+                }
+                this.#isRejected = true;
+                if (this.#reject) this.#reject(reason);
+            };
+
+            const onCancel = (cancelHandler: () => void): void => {
+                if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+                    return;
+                }
+                this.#cancelHandlers.push(cancelHandler);
+            };
+
+            Object.defineProperty(onCancel, 'isResolved', {
+                get: (): boolean => this.#isResolved,
+            });
+
+            Object.defineProperty(onCancel, 'isRejected', {
+                get: (): boolean => this.#isRejected,
+            });
+
+            Object.defineProperty(onCancel, 'isCancelled', {
+                get: (): boolean => this.#isCancelled,
+            });
+
+            return executor(onResolve, onReject, onCancel as OnCancel);
+        });
+    }
+
+     get [Symbol.toStringTag]() {
+            return "Cancellable Promise";
+     }
+
+    public then<TResult1 = T, TResult2 = never>(
+        onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
+        onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
+    ): Promise<TResult1 | TResult2> {
+        return this.#promise.then(onFulfilled, onRejected);
+    }
+
+    public catch<TResult = never>(
+        onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
+    ): Promise<T | TResult> {
+        return this.#promise.catch(onRejected);
+    }
+
+    public finally(onFinally?: (() => void) | null): Promise<T> {
+        return this.#promise.finally(onFinally);
+    }
+
+    public cancel(): void {
+        if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+            return;
+        }
+        this.#isCancelled = true;
+        if (this.#cancelHandlers.length) {
+            try {
+                for (const cancelHandler of this.#cancelHandlers) {
+                    cancelHandler();
+                }
+            } catch (error) {
+                console.warn('Cancellation threw an error', error);
+                return;
+            }
+        }
+        this.#cancelHandlers.length = 0;
+        if (this.#reject) this.#reject(new CancelError('Request aborted'));
+    }
+
+    public get isCancelled(): boolean {
+        return this.#isCancelled;
+    }
+}
diff --git a/src/client/resource/core/OpenAPI.ts b/src/client/resource/core/OpenAPI.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b53d3b96ba6dd7e71fd2068a1a9e3a0a139eab6
--- /dev/null
+++ b/src/client/resource/core/OpenAPI.ts
@@ -0,0 +1,32 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+
+type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
+type Headers = Record<string, string>;
+
+export type OpenAPIConfig = {
+    BASE: string;
+    VERSION: string;
+    WITH_CREDENTIALS: boolean;
+    CREDENTIALS: 'include' | 'omit' | 'same-origin';
+    TOKEN?: string | Resolver<string> | undefined;
+    USERNAME?: string | Resolver<string> | undefined;
+    PASSWORD?: string | Resolver<string> | undefined;
+    HEADERS?: Headers | Resolver<Headers> | undefined;
+    ENCODE_PATH?: ((path: string) => string) | undefined;
+};
+
+export const OpenAPI: OpenAPIConfig = {
+    BASE: '/api/resource-service',
+    VERSION: '1.0.0',
+    WITH_CREDENTIALS: false,
+    CREDENTIALS: 'include',
+    TOKEN: undefined,
+    USERNAME: undefined,
+    PASSWORD: undefined,
+    HEADERS: undefined,
+    ENCODE_PATH: undefined,
+};
diff --git a/src/client/resource/core/request.ts b/src/client/resource/core/request.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c6a0602a006d2d1855c2ab50d2d7f43c2eb1bab4
--- /dev/null
+++ b/src/client/resource/core/request.ts
@@ -0,0 +1,322 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import axios from 'axios';
+import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
+import FormData from 'form-data';
+
+import { ApiError } from './ApiError';
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { ApiResult } from './ApiResult';
+import { CancelablePromise } from './CancelablePromise';
+import type { OnCancel } from './CancelablePromise';
+import type { OpenAPIConfig } from './OpenAPI';
+
+export const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {
+    return value !== undefined && value !== null;
+};
+
+export const isString = (value: any): value is string => {
+    return typeof value === 'string';
+};
+
+export const isStringWithValue = (value: any): value is string => {
+    return isString(value) && value !== '';
+};
+
+export const isBlob = (value: any): value is Blob => {
+    return (
+        typeof value === 'object' &&
+        typeof value.type === 'string' &&
+        typeof value.stream === 'function' &&
+        typeof value.arrayBuffer === 'function' &&
+        typeof value.constructor === 'function' &&
+        typeof value.constructor.name === 'string' &&
+        /^(Blob|File)$/.test(value.constructor.name) &&
+        /^(Blob|File)$/.test(value[Symbol.toStringTag])
+    );
+};
+
+export const isFormData = (value: any): value is FormData => {
+    return value instanceof FormData;
+};
+
+export const isSuccess = (status: number): boolean => {
+    return status >= 200 && status < 300;
+};
+
+export const base64 = (str: string): string => {
+    try {
+        return btoa(str);
+    } catch (err) {
+        // @ts-ignore
+        return Buffer.from(str).toString('base64');
+    }
+};
+
+export const getQueryString = (params: Record<string, any>): string => {
+    const qs: string[] = [];
+
+    const append = (key: string, value: any) => {
+        qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
+    };
+
+    const process = (key: string, value: any) => {
+        if (isDefined(value)) {
+            if (Array.isArray(value)) {
+                value.forEach(v => {
+                    process(key, v);
+                });
+            } else if (typeof value === 'object') {
+                Object.entries(value).forEach(([k, v]) => {
+                    process(`${key}[${k}]`, v);
+                });
+            } else {
+                append(key, value);
+            }
+        }
+    };
+
+    Object.entries(params).forEach(([key, value]) => {
+        process(key, value);
+    });
+
+    if (qs.length > 0) {
+        return `?${qs.join('&')}`;
+    }
+
+    return '';
+};
+
+const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
+    const encoder = config.ENCODE_PATH || encodeURI;
+
+    const path = options.url
+        .replace('{api-version}', config.VERSION)
+        .replace(/{(.*?)}/g, (substring: string, group: string) => {
+            if (options.path?.hasOwnProperty(group)) {
+                return encoder(String(options.path[group]));
+            }
+            return substring;
+        });
+
+    const url = `${config.BASE}${path}`;
+    if (options.query) {
+        return `${url}${getQueryString(options.query)}`;
+    }
+    return url;
+};
+
+export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
+    if (options.formData) {
+        const formData = new FormData();
+
+        const process = (key: string, value: any) => {
+            if (isString(value) || isBlob(value)) {
+                formData.append(key, value);
+            } else {
+                formData.append(key, JSON.stringify(value));
+            }
+        };
+
+        Object.entries(options.formData)
+            .filter(([_, value]) => isDefined(value))
+            .forEach(([key, value]) => {
+                if (Array.isArray(value)) {
+                    value.forEach(v => process(key, v));
+                } else {
+                    process(key, value);
+                }
+            });
+
+        return formData;
+    }
+    return undefined;
+};
+
+type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
+
+export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {
+    if (typeof resolver === 'function') {
+        return (resolver as Resolver<T>)(options);
+    }
+    return resolver;
+};
+
+export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
+    const [token, username, password, additionalHeaders] = await Promise.all([
+        resolve(options, config.TOKEN),
+        resolve(options, config.USERNAME),
+        resolve(options, config.PASSWORD),
+        resolve(options, config.HEADERS),
+    ]);
+
+    const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
+
+    const headers = Object.entries({
+        Accept: 'application/json',
+        ...additionalHeaders,
+        ...options.headers,
+        ...formHeaders,
+    })
+    .filter(([_, value]) => isDefined(value))
+    .reduce((headers, [key, value]) => ({
+        ...headers,
+        [key]: String(value),
+    }), {} as Record<string, string>);
+
+    if (isStringWithValue(token)) {
+        headers['Authorization'] = `Bearer ${token}`;
+    }
+
+    if (isStringWithValue(username) && isStringWithValue(password)) {
+        const credentials = base64(`${username}:${password}`);
+        headers['Authorization'] = `Basic ${credentials}`;
+    }
+
+    if (options.body) {
+        if (options.mediaType) {
+            headers['Content-Type'] = options.mediaType;
+        } else if (isBlob(options.body)) {
+            headers['Content-Type'] = options.body.type || 'application/octet-stream';
+        } else if (isString(options.body)) {
+            headers['Content-Type'] = 'text/plain';
+        } else if (!isFormData(options.body)) {
+            headers['Content-Type'] = 'application/json';
+        }
+    }
+
+    return headers;
+};
+
+export const getRequestBody = (options: ApiRequestOptions): any => {
+    if (options.body) {
+        return options.body;
+    }
+    return undefined;
+};
+
+export const sendRequest = async <T>(
+    config: OpenAPIConfig,
+    options: ApiRequestOptions,
+    url: string,
+    body: any,
+    formData: FormData | undefined,
+    headers: Record<string, string>,
+    onCancel: OnCancel,
+    axiosClient: AxiosInstance
+): Promise<AxiosResponse<T>> => {
+    const source = axios.CancelToken.source();
+
+    const requestConfig: AxiosRequestConfig = {
+        url,
+        headers,
+        data: body ?? formData,
+        method: options.method,
+        withCredentials: config.WITH_CREDENTIALS,
+        cancelToken: source.token,
+    };
+
+    onCancel(() => source.cancel('The user aborted a request.'));
+
+    try {
+        return await axiosClient.request(requestConfig);
+    } catch (error) {
+        const axiosError = error as AxiosError<T>;
+        if (axiosError.response) {
+            return axiosError.response;
+        }
+        throw error;
+    }
+};
+
+export const getResponseHeader = (response: AxiosResponse<any>, responseHeader?: string): string | undefined => {
+    if (responseHeader) {
+        const content = response.headers[responseHeader];
+        if (isString(content)) {
+            return content;
+        }
+    }
+    return undefined;
+};
+
+export const getResponseBody = (response: AxiosResponse<any>): any => {
+    if (response.status !== 204) {
+        return response.data;
+    }
+    return undefined;
+};
+
+export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
+    const errors: Record<number, string> = {
+        400: 'Bad Request',
+        401: 'Unauthorized',
+        403: 'Forbidden',
+        404: 'Not Found',
+        500: 'Internal Server Error',
+        502: 'Bad Gateway',
+        503: 'Service Unavailable',
+        ...options.errors,
+    }
+
+    const error = errors[result.status];
+    if (error) {
+        throw new ApiError(options, result, error);
+    }
+
+    if (!result.ok) {
+        const errorStatus = result.status ?? 'unknown';
+        const errorStatusText = result.statusText ?? 'unknown';
+        const errorBody = (() => {
+            try {
+                return JSON.stringify(result.body, null, 2);
+            } catch (e) {
+                return undefined;
+            }
+        })();
+
+        throw new ApiError(options, result,
+            `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
+        );
+    }
+};
+
+/**
+ * Request method
+ * @param config The OpenAPI configuration object
+ * @param options The request options from the service
+ * @param axiosClient The axios client instance to use
+ * @returns CancelablePromise<T>
+ * @throws ApiError
+ */
+export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => {
+    return new CancelablePromise(async (resolve, reject, onCancel) => {
+        try {
+            const url = getUrl(config, options);
+            const formData = getFormData(options);
+            const body = getRequestBody(options);
+            const headers = await getHeaders(config, options, formData);
+
+            if (!onCancel.isCancelled) {
+                const response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient);
+                const responseBody = getResponseBody(response);
+                const responseHeader = getResponseHeader(response, options.responseHeader);
+
+                const result: ApiResult = {
+                    url,
+                    ok: isSuccess(response.status),
+                    status: response.status,
+                    statusText: response.statusText,
+                    body: responseHeader ?? responseBody,
+                };
+
+                catchErrorCodes(options, result);
+
+                resolve(result.body);
+            }
+        } catch (error) {
+            reject(error);
+        }
+    });
+};
diff --git a/src/client/resource/index.ts b/src/client/resource/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b09145551bb2ba92b29464bf79439a76544bed28
--- /dev/null
+++ b/src/client/resource/index.ts
@@ -0,0 +1,20 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export { ApiError } from './core/ApiError';
+export { CancelablePromise, CancelError } from './core/CancelablePromise';
+export { OpenAPI } from './core/OpenAPI';
+export type { OpenAPIConfig } from './core/OpenAPI';
+
+export type { ErrorDetail } from './models/ErrorDetail';
+export type { HTTPValidationError } from './models/HTTPValidationError';
+export type { ResourceIn } from './models/ResourceIn';
+export type { ResourceOut } from './models/ResourceOut';
+export type { ResourceVersionIn } from './models/ResourceVersionIn';
+export type { ResourceVersionOut } from './models/ResourceVersionOut';
+export { Status } from './models/Status';
+export type { ValidationError } from './models/ValidationError';
+
+export { ResourceService } from './services/ResourceService';
+export { ResourceVersionService } from './services/ResourceVersionService';
diff --git a/src/client/resource/models/ErrorDetail.ts b/src/client/resource/models/ErrorDetail.ts
new file mode 100644
index 0000000000000000000000000000000000000000..69dd37eb86bc15e2ab2037f3f82516493801e74d
--- /dev/null
+++ b/src/client/resource/models/ErrorDetail.ts
@@ -0,0 +1,14 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Schema for a error due to a rejected request.
+ */
+export type ErrorDetail = {
+    /**
+     * Detail about the occurred error
+     */
+    detail: string;
+};
+
diff --git a/src/client/resource/models/HTTPValidationError.ts b/src/client/resource/models/HTTPValidationError.ts
new file mode 100644
index 0000000000000000000000000000000000000000..892e4257ccbb49769737cb2e78e0aad49d81a3b1
--- /dev/null
+++ b/src/client/resource/models/HTTPValidationError.ts
@@ -0,0 +1,9 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ValidationError } from './ValidationError';
+export type HTTPValidationError = {
+    detail?: Array<ValidationError>;
+};
+
diff --git a/src/client/resource/models/ResourceIn.ts b/src/client/resource/models/ResourceIn.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bb3eddc4e2b8bc0763e0e1f71eb2b51d074e7e9d
--- /dev/null
+++ b/src/client/resource/models/ResourceIn.ts
@@ -0,0 +1,23 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ResourceIn = {
+    /**
+     * Short tag describing the version of the resource
+     */
+    release: string;
+    /**
+     * Short Name for the resource
+     */
+    name: string;
+    /**
+     * Short description for this resource
+     */
+    description: string;
+    /**
+     * A link or similar where the resource originates from
+     */
+    source: string;
+};
+
diff --git a/src/client/resource/models/ResourceOut.ts b/src/client/resource/models/ResourceOut.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dcb34557a6459d8d4a98764ce9376417ead6e4f7
--- /dev/null
+++ b/src/client/resource/models/ResourceOut.ts
@@ -0,0 +1,32 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ResourceVersionOut } from './ResourceVersionOut';
+export type ResourceOut = {
+    /**
+     * Short Name for the resource
+     */
+    name: string;
+    /**
+     * Short description for this resource
+     */
+    description: string;
+    /**
+     * A link or similar where the resource originates from
+     */
+    source: string;
+    /**
+     * ID of the resource
+     */
+    resource_id: string;
+    /**
+     * ID of the maintainer
+     */
+    maintainer_id: string;
+    /**
+     * Versions of the resource
+     */
+    versions: Array<ResourceVersionOut>;
+};
+
diff --git a/src/client/resource/models/ResourceVersionIn.ts b/src/client/resource/models/ResourceVersionIn.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68eb986b9852ca442172b4d9ac7ba2b1e33d8d00
--- /dev/null
+++ b/src/client/resource/models/ResourceVersionIn.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ResourceVersionIn = {
+    /**
+     * Short tag describing the version of the resource
+     */
+    release: string;
+};
+
diff --git a/src/client/resource/models/ResourceVersionOut.ts b/src/client/resource/models/ResourceVersionOut.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7ab1dda6759829a8e9a4e3c4b0b9cf3658c8cb56
--- /dev/null
+++ b/src/client/resource/models/ResourceVersionOut.ts
@@ -0,0 +1,36 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { Status } from './Status';
+export type ResourceVersionOut = {
+    /**
+     * Short tag describing the version of the resource
+     */
+    release: string;
+    /**
+     * Status of the resource version
+     */
+    status: Status;
+    /**
+     * ID of the resource version
+     */
+    resource_version_id: string;
+    /**
+     * ID of the resource
+     */
+    resource_id: string;
+    /**
+     * Timestamp when the version was created as UNIX timestamp
+     */
+    created_at: number;
+    /**
+     * Path to the resource on the cluster if the resource is synchronized
+     */
+    readonly cluster_path: (string | null);
+    /**
+     * Path to the resource in the S3 Bucket. Not publicly available.
+     */
+    readonly s3_path: string;
+};
+
diff --git a/src/client/resource/models/Status.ts b/src/client/resource/models/Status.ts
new file mode 100644
index 0000000000000000000000000000000000000000..51aaaaac28610eeb989f2d761d1d9f525cdd8c4b
--- /dev/null
+++ b/src/client/resource/models/Status.ts
@@ -0,0 +1,17 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Enumeration for the possible status of a resource version.
+ */
+export enum Status {
+    RESOURCE_REQUESTED = 'RESOURCE_REQUESTED',
+    SYNC_REQUESTED = 'SYNC_REQUESTED',
+    SYNCHRONIZING = 'SYNCHRONIZING',
+    SYNCHRONIZED = 'SYNCHRONIZED',
+    LATEST = 'LATEST',
+    DENIED = 'DENIED',
+    CLUSTER_DELETED = 'CLUSTER_DELETED',
+    S3_DELETED = 'S3_DELETED',
+}
diff --git a/src/client/resource/models/ValidationError.ts b/src/client/resource/models/ValidationError.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f2ff49a2b9ad95d18a90c95dc2a7c0b60b7e85e7
--- /dev/null
+++ b/src/client/resource/models/ValidationError.ts
@@ -0,0 +1,10 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ValidationError = {
+    loc: Array<(string | number)>;
+    msg: string;
+    type: string;
+};
+
diff --git a/src/client/resource/services/ResourceService.ts b/src/client/resource/services/ResourceService.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c182a2bdc2b2aec492e95d0f6b0b26951b367cd
--- /dev/null
+++ b/src/client/resource/services/ResourceService.ts
@@ -0,0 +1,126 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ResourceIn } from '../models/ResourceIn';
+import type { ResourceOut } from '../models/ResourceOut';
+import type { Status } from '../models/Status';
+import type { CancelablePromise } from '../core/CancelablePromise';
+import { OpenAPI } from '../core/OpenAPI';
+import { request as __request } from '../core/request';
+export class ResourceService {
+    /**
+     * List resources
+     * List all resources.
+     *
+     * Permission `resource:list` required.
+     * @param maintainerId Filter for resource by maintainer. If current user is the same as maintainer ID, permission `resource:list` required, otherwise `resource:list_filter`.
+     * @param versionStatus Which versions of the resource to include in the response. Permission `resource:list_filter` required, unless `maintainer_id` is provided and current user is maintainer, then only permission `resource:list` required. Default `LATEST` and `SYNCHRONIZED`.
+     * @param nameSubstring
+     * @returns ResourceOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceListResources(
+        maintainerId?: string,
+        versionStatus?: Array<Status>,
+        nameSubstring?: string,
+    ): CancelablePromise<Array<ResourceOut>> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/resources',
+            query: {
+                'maintainer_id': maintainerId,
+                'version_status': versionStatus,
+                'name_substring': nameSubstring,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Request a new resource
+     * Request a new resources.
+     *
+     * Permission `resource:create` required.
+     * @param requestBody
+     * @returns ResourceOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceCreateResource(
+        requestBody: ResourceIn,
+    ): CancelablePromise<ResourceOut> {
+        return __request(OpenAPI, {
+            method: 'POST',
+            url: '/resources',
+            body: requestBody,
+            mediaType: 'application/json',
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Get a resource
+     * Get a specific resource.
+     *
+     * Permission `resource:read` required.
+     * @param rid
+     * @param versionStatus Which versions of the resource to include in the response. Permission `resource:read_any` required, unless the current user is the maintainer, then only permission `resource:read` required. Default `LATEST` and `SYNCHRONIZED`.
+     * @returns ResourceOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceGetResource(
+        rid: string,
+        versionStatus?: Array<Status>,
+    ): CancelablePromise<ResourceOut> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/resources/{rid}',
+            path: {
+                'rid': rid,
+            },
+            query: {
+                'version_status': versionStatus,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Delete a resource
+     * Delete a resources.
+     *
+     * Permission `resource:delete` required.
+     * @param rid
+     * @returns void
+     * @throws ApiError
+     */
+    public static resourceDeleteResource(
+        rid: string,
+    ): CancelablePromise<void> {
+        return __request(OpenAPI, {
+            method: 'DELETE',
+            url: '/resources/{rid}',
+            path: {
+                'rid': rid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+}
diff --git a/src/client/resource/services/ResourceVersionService.ts b/src/client/resource/services/ResourceVersionService.ts
new file mode 100644
index 0000000000000000000000000000000000000000..804d721aaab59aea45dff36fda2481770c257149
--- /dev/null
+++ b/src/client/resource/services/ResourceVersionService.ts
@@ -0,0 +1,248 @@
+/* generated using openapi-typescript-codegen -- do no edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ResourceVersionIn } from '../models/ResourceVersionIn';
+import type { ResourceVersionOut } from '../models/ResourceVersionOut';
+import type { Status } from '../models/Status';
+import type { CancelablePromise } from '../core/CancelablePromise';
+import { OpenAPI } from '../core/OpenAPI';
+import { request as __request } from '../core/request';
+export class ResourceVersionService {
+    /**
+     * List versions of a resource
+     * List all the resource version for a specific resource.
+     *
+     * Permission 'resource:read' required.
+     * @param rid
+     * @param versionStatus Which versions to include in the response. Permission `resource:read_any` required, current user is the maintainer, then only permission `resource:read` required. Default `LATEST` and `SYNCHRONIZED`.
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionListResourceVersions(
+        rid: string,
+        versionStatus?: Array<Status>,
+    ): CancelablePromise<Array<ResourceVersionOut>> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/resources/{rid}/versions',
+            path: {
+                'rid': rid,
+            },
+            query: {
+                'version_status': versionStatus,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Request new version of a resource
+     * Request a new resource version.
+     *
+     * Permission `resource:update` required if the current user is the maintainer, `resource:update_any` otherwise.
+     * @param rid
+     * @param requestBody
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionRequestResourceVersion(
+        rid: string,
+        requestBody: ResourceVersionIn,
+    ): CancelablePromise<ResourceVersionOut> {
+        return __request(OpenAPI, {
+            method: 'POST',
+            url: '/resources/{rid}/versions',
+            path: {
+                'rid': rid,
+            },
+            body: requestBody,
+            mediaType: 'application/json',
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Get version of a resource
+     * Get a specific resource version for a specific resource.
+     *
+     * Permission `resource:read` required. If the status of the resource version is not `LATEST` or `SYNCHRONIZED` and
+     * the current user is not the maintainer, then the permission `resource:read_any` is required.
+     * @param rid
+     * @param rvid
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionGetResourceVersion(
+        rid: string,
+        rvid: string,
+    ): CancelablePromise<ResourceVersionOut> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/resources/{rid}/versions/{rvid}',
+            path: {
+                'rid': rid,
+                'rvid': rvid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Request resource version synchronization
+     * Request the synchronization of a resource version to the cluster.
+     *
+     * Permission `resource:request_sync` required if current user is the maintainer, `resource:request_sync_any` otherwise.
+     * @param rid
+     * @param rvid
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionRequestResourceVersionSync(
+        rid: string,
+        rvid: string,
+    ): CancelablePromise<ResourceVersionOut> {
+        return __request(OpenAPI, {
+            method: 'PUT',
+            url: '/resources/{rid}/versions/{rvid}/request_sync',
+            path: {
+                'rid': rid,
+                'rvid': rvid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Synchronize resource version with cluster
+     * Synchronize the resource version to the cluster.
+     *
+     * Permission `resource:sync` required.
+     * @param rid
+     * @param rvid
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionResourceVersionSync(
+        rid: string,
+        rvid: string,
+    ): CancelablePromise<ResourceVersionOut> {
+        return __request(OpenAPI, {
+            method: 'PUT',
+            url: '/resources/{rid}/versions/{rvid}/sync',
+            path: {
+                'rid': rid,
+                'rvid': rvid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Set resource version to latest
+     * Set the resource version as the latest version.
+     *
+     * Permission `resource:set_latest` required.
+     * @param rid
+     * @param rvid
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionResourceVersionLatest(
+        rid: string,
+        rvid: string,
+    ): CancelablePromise<ResourceVersionOut> {
+        return __request(OpenAPI, {
+            method: 'PUT',
+            url: '/resources/{rid}/versions/{rvid}/latest',
+            path: {
+                'rid': rid,
+                'rvid': rvid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Delete resource version on cluster
+     * Delete the resource version on the cluster.
+     *
+     * Permission `resource:delete_cluster` required.
+     * @param rid
+     * @param rvid
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionDeleteResourceVersionCluster(
+        rid: string,
+        rvid: string,
+    ): CancelablePromise<ResourceVersionOut> {
+        return __request(OpenAPI, {
+            method: 'DELETE',
+            url: '/resources/{rid}/versions/{rvid}/cluster',
+            path: {
+                'rid': rid,
+                'rvid': rvid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+    /**
+     * Delete resource version in S3
+     * Delete the resource version in the S3 bucket.
+     *
+     * Permission `resource:delete_s3` required.
+     * @param rid
+     * @param rvid
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionDeleteResourceVersionS3(
+        rid: string,
+        rvid: string,
+    ): CancelablePromise<ResourceVersionOut> {
+        return __request(OpenAPI, {
+            method: 'DELETE',
+            url: '/resources/{rid}/versions/{rvid}/s3',
+            path: {
+                'rid': rid,
+                'rvid': rvid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
+}
diff --git a/src/client/s3proxy/core/CancelablePromise.ts b/src/client/s3proxy/core/CancelablePromise.ts
index 55fef8517238d2dab7478598eefca72e657b9fb5..eb02246c31f61b897667a6433d55b8b413bafbc1 100644
--- a/src/client/s3proxy/core/CancelablePromise.ts
+++ b/src/client/s3proxy/core/CancelablePromise.ts
@@ -51,7 +51,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isResolved = true;
-                this.#resolve?.(value);
+                if (this.#resolve) this.#resolve(value);
             };
 
             const onReject = (reason?: any): void => {
@@ -59,7 +59,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isRejected = true;
-                this.#reject?.(reason);
+                if (this.#reject) this.#reject(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
@@ -122,7 +122,7 @@ export class CancelablePromise<T> implements Promise<T> {
             }
         }
         this.#cancelHandlers.length = 0;
-        this.#reject?.(new CancelError('Request aborted'));
+        if (this.#reject) this.#reject(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
diff --git a/src/client/s3proxy/core/request.ts b/src/client/s3proxy/core/request.ts
index 1142d43219797c94e09f35ba83a9f5441892ddd2..c6a0602a006d2d1855c2ab50d2d7f43c2eb1bab4 100644
--- a/src/client/s3proxy/core/request.ts
+++ b/src/client/s3proxy/core/request.ts
@@ -145,10 +145,13 @@ export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Reso
 };
 
 export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
-    const token = await resolve(options, config.TOKEN);
-    const username = await resolve(options, config.USERNAME);
-    const password = await resolve(options, config.PASSWORD);
-    const additionalHeaders = await resolve(options, config.HEADERS);
+    const [token, username, password, additionalHeaders] = await Promise.all([
+        resolve(options, config.TOKEN),
+        resolve(options, config.USERNAME),
+        resolve(options, config.PASSWORD),
+        resolve(options, config.HEADERS),
+    ]);
+
     const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
 
     const headers = Object.entries({
diff --git a/src/client/s3proxy/models/BucketIn.ts b/src/client/s3proxy/models/BucketIn.ts
index 96ff7b44981b9551e21fd5562e8d82caeb080043..9505a6420088ebf53b6601e1700d92a865272eb0 100644
--- a/src/client/s3proxy/models/BucketIn.ts
+++ b/src/client/s3proxy/models/BucketIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for creating a new bucket.
  */
diff --git a/src/client/s3proxy/models/BucketOut.ts b/src/client/s3proxy/models/BucketOut.ts
index 5b38801ea29b9a411113b9b5c2343d33c6f1ef15..cbb67fb5d8ca30e046785ffb35be521c81e49937 100644
--- a/src/client/s3proxy/models/BucketOut.ts
+++ b/src/client/s3proxy/models/BucketOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Constraint } from './Constraint';
-
 /**
  * Schema for answering a request with a bucket.
  */
diff --git a/src/client/s3proxy/models/BucketPermissionIn.ts b/src/client/s3proxy/models/BucketPermissionIn.ts
index 06bd223c69ce58189c1699343b1018187a8942c1..e0e38255d47fedaf9bf6abda58b1e8e5e5537ef5 100644
--- a/src/client/s3proxy/models/BucketPermissionIn.ts
+++ b/src/client/s3proxy/models/BucketPermissionIn.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Permission } from './Permission';
-
 export type BucketPermissionIn = {
     /**
      * Start date of permission as UNIX timestamp
diff --git a/src/client/s3proxy/models/BucketPermissionOut.ts b/src/client/s3proxy/models/BucketPermissionOut.ts
index 38ac0254a4332ab86e098af5767e16cd022d0d80..ec3c79d39c434447e2df2791206f05f5f278f909 100644
--- a/src/client/s3proxy/models/BucketPermissionOut.ts
+++ b/src/client/s3proxy/models/BucketPermissionOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Permission } from './Permission';
-
 /**
  * Schema for the bucket permissions.
  */
diff --git a/src/client/s3proxy/models/BucketPermissionParameters.ts b/src/client/s3proxy/models/BucketPermissionParameters.ts
index 4d8ef97bf2f6c6f51fbb3a1c4f2937fba8a0aefb..47c7576e18ad4ec0e6ff29040c723153eeee612b 100644
--- a/src/client/s3proxy/models/BucketPermissionParameters.ts
+++ b/src/client/s3proxy/models/BucketPermissionParameters.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Permission } from './Permission';
-
 /**
  * Schema for the parameters of a bucket permission.
  */
diff --git a/src/client/s3proxy/models/BucketType.ts b/src/client/s3proxy/models/BucketType.ts
index b096c70b0b7234bc57dd1ecc2223bd41aa3e0d08..8001a27664273bd2a5ad360f6f10444be2e7046d 100644
--- a/src/client/s3proxy/models/BucketType.ts
+++ b/src/client/s3proxy/models/BucketType.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the type of buckets to fetch from the DB
  *
diff --git a/src/client/s3proxy/models/Constraint.ts b/src/client/s3proxy/models/Constraint.ts
index afa995a983b5e08b66d55b8dfa4243553351d3a7..99043221bd2e12d3e2a112ae9308437d99e7df40 100644
--- a/src/client/s3proxy/models/Constraint.ts
+++ b/src/client/s3proxy/models/Constraint.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the possible permission on a bucket.
  */
diff --git a/src/client/s3proxy/models/ErrorDetail.ts b/src/client/s3proxy/models/ErrorDetail.ts
index eba50ab9335b70df9b29cada4c54cf9ffaad10a4..69dd37eb86bc15e2ab2037f3f82516493801e74d 100644
--- a/src/client/s3proxy/models/ErrorDetail.ts
+++ b/src/client/s3proxy/models/ErrorDetail.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a error due to a rejected request.
  */
diff --git a/src/client/s3proxy/models/HTTPValidationError.ts b/src/client/s3proxy/models/HTTPValidationError.ts
index c0bcc87cf7f3222638466bc2e5753ca1accf01f7..892e4257ccbb49769737cb2e78e0aad49d81a3b1 100644
--- a/src/client/s3proxy/models/HTTPValidationError.ts
+++ b/src/client/s3proxy/models/HTTPValidationError.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { ValidationError } from './ValidationError';
-
 export type HTTPValidationError = {
     detail?: Array<ValidationError>;
 };
diff --git a/src/client/s3proxy/models/Permission.ts b/src/client/s3proxy/models/Permission.ts
index 00592a496a01e178e7192ba3bd76c51b29a3a138..33aebf753f36ab7b85a50e0de9d47501f3ff8709 100644
--- a/src/client/s3proxy/models/Permission.ts
+++ b/src/client/s3proxy/models/Permission.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the possible permission on a bucket.
  */
diff --git a/src/client/s3proxy/models/PermissionStatus.ts b/src/client/s3proxy/models/PermissionStatus.ts
index d51e0928ec89e662e033c833ceacd5bd6c66bc74..0d858f08edd1fdb664ee83cc5805423dc3dd93f5 100644
--- a/src/client/s3proxy/models/PermissionStatus.ts
+++ b/src/client/s3proxy/models/PermissionStatus.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Status of a bucket permission. Can be either `ACTIVE` or `INACTIVE`. A permission can only get `INACTIVE` if the
  * permission itself has a time limit and the current time is not in the timespan.
diff --git a/src/client/s3proxy/models/S3Key.ts b/src/client/s3proxy/models/S3Key.ts
index cc9f01a57583423aeaad174dc68c8e96c16706a9..b3c30da182357c5a2d3b3c07c315c3cc3a6ff917 100644
--- a/src/client/s3proxy/models/S3Key.ts
+++ b/src/client/s3proxy/models/S3Key.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a S3 key associated with a user.
  */
diff --git a/src/client/s3proxy/models/ValidationError.ts b/src/client/s3proxy/models/ValidationError.ts
index 18997ec72f4103731f38d915508522ba23ba8506..f2ff49a2b9ad95d18a90c95dc2a7c0b60b7e85e7 100644
--- a/src/client/s3proxy/models/ValidationError.ts
+++ b/src/client/s3proxy/models/ValidationError.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ValidationError = {
     loc: Array<(string | number)>;
     msg: string;
diff --git a/src/client/s3proxy/services/BucketPermissionService.ts b/src/client/s3proxy/services/BucketPermissionService.ts
index 3b55d1bc1c6674483f24e3471fd5ae07d4cfa189..d5119dc321fff3d195d7638805baa255aefaad6f 100644
--- a/src/client/s3proxy/services/BucketPermissionService.ts
+++ b/src/client/s3proxy/services/BucketPermissionService.ts
@@ -7,13 +7,10 @@ import type { BucketPermissionOut } from '../models/BucketPermissionOut';
 import type { BucketPermissionParameters } from '../models/BucketPermissionParameters';
 import type { Permission } from '../models/Permission';
 import type { PermissionStatus } from '../models/PermissionStatus';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class BucketPermissionService {
-
     /**
      * Get permission for bucket and user combination.
      * Get the bucket permissions for the specific combination of bucket and user.
@@ -46,7 +43,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Delete a bucket permission
      * Delete the bucket permissions for the specific combination of bucket and user.
@@ -79,7 +75,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Update a bucket permission
      * Update a permission for a bucket and user.
@@ -114,7 +109,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Get all permissions for a bucket.
      * List all the bucket permissions for the given bucket.
@@ -150,7 +144,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Get all permissions for a user.
      * List all the bucket permissions for the given user.
@@ -186,7 +179,6 @@ export class BucketPermissionService {
             },
         });
     }
-
     /**
      * Create a permission.
      * Create a permission for a bucket and user.
@@ -212,5 +204,4 @@ export class BucketPermissionService {
             },
         });
     }
-
 }
diff --git a/src/client/s3proxy/services/BucketService.ts b/src/client/s3proxy/services/BucketService.ts
index d51b66804563285d8e72e555076407af7ee381d9..92cee996275bb497a6151023f844736cb7d6a8c5 100644
--- a/src/client/s3proxy/services/BucketService.ts
+++ b/src/client/s3proxy/services/BucketService.ts
@@ -5,13 +5,10 @@
 import type { BucketIn } from '../models/BucketIn';
 import type { BucketOut } from '../models/BucketOut';
 import type { BucketType } from '../models/BucketType';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class BucketService {
-
     /**
      * List buckets of user
      * List all the buckets in the system or of the desired user where the user has READ permissions for.
@@ -41,7 +38,6 @@ export class BucketService {
             },
         });
     }
-
     /**
      * Create a bucket for the current user
      * Create a bucket for the current user.
@@ -71,7 +67,6 @@ export class BucketService {
             },
         });
     }
-
     /**
      * Get a bucket by its name
      * Get a bucket by its name if the current user has READ permissions for the bucket.
@@ -99,7 +94,6 @@ export class BucketService {
             },
         });
     }
-
     /**
      * Delete a bucket
      * Delete a bucket by its name. Only the owner of the bucket can delete the bucket.
@@ -132,5 +126,4 @@ export class BucketService {
             },
         });
     }
-
 }
diff --git a/src/client/s3proxy/services/S3KeyService.ts b/src/client/s3proxy/services/S3KeyService.ts
index c8e048bdc22fbbd683259c5850045063938a7aee..732fd680c4e5530909a9e67fb3d5eb28137d057f 100644
--- a/src/client/s3proxy/services/S3KeyService.ts
+++ b/src/client/s3proxy/services/S3KeyService.ts
@@ -3,13 +3,10 @@
 /* tslint:disable */
 /* eslint-disable */
 import type { S3Key } from '../models/S3Key';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class S3KeyService {
-
     /**
      * Get the S3 Access keys from a user
      * Get all the S3 Access keys for a specific user.
@@ -36,7 +33,6 @@ export class S3KeyService {
             },
         });
     }
-
     /**
      * Create a Access key for a user
      * Create a S3 Access key for a specific user.
@@ -63,7 +59,6 @@ export class S3KeyService {
             },
         });
     }
-
     /**
      * Get a specific S3 Access key from a user
      * Get a specific S3 Access Key for a specific user.
@@ -93,7 +88,6 @@ export class S3KeyService {
             },
         });
     }
-
     /**
      * Delete a specific S3 Access key from a user
      * Delete a specific S3 Access key for a specific user.
@@ -123,5 +117,4 @@ export class S3KeyService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/core/CancelablePromise.ts b/src/client/workflow/core/CancelablePromise.ts
index 55fef8517238d2dab7478598eefca72e657b9fb5..eb02246c31f61b897667a6433d55b8b413bafbc1 100644
--- a/src/client/workflow/core/CancelablePromise.ts
+++ b/src/client/workflow/core/CancelablePromise.ts
@@ -51,7 +51,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isResolved = true;
-                this.#resolve?.(value);
+                if (this.#resolve) this.#resolve(value);
             };
 
             const onReject = (reason?: any): void => {
@@ -59,7 +59,7 @@ export class CancelablePromise<T> implements Promise<T> {
                     return;
                 }
                 this.#isRejected = true;
-                this.#reject?.(reason);
+                if (this.#reject) this.#reject(reason);
             };
 
             const onCancel = (cancelHandler: () => void): void => {
@@ -122,7 +122,7 @@ export class CancelablePromise<T> implements Promise<T> {
             }
         }
         this.#cancelHandlers.length = 0;
-        this.#reject?.(new CancelError('Request aborted'));
+        if (this.#reject) this.#reject(new CancelError('Request aborted'));
     }
 
     public get isCancelled(): boolean {
diff --git a/src/client/workflow/core/request.ts b/src/client/workflow/core/request.ts
index 1142d43219797c94e09f35ba83a9f5441892ddd2..c6a0602a006d2d1855c2ab50d2d7f43c2eb1bab4 100644
--- a/src/client/workflow/core/request.ts
+++ b/src/client/workflow/core/request.ts
@@ -145,10 +145,13 @@ export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Reso
 };
 
 export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
-    const token = await resolve(options, config.TOKEN);
-    const username = await resolve(options, config.USERNAME);
-    const password = await resolve(options, config.PASSWORD);
-    const additionalHeaders = await resolve(options, config.HEADERS);
+    const [token, username, password, additionalHeaders] = await Promise.all([
+        resolve(options, config.TOKEN),
+        resolve(options, config.USERNAME),
+        resolve(options, config.PASSWORD),
+        resolve(options, config.HEADERS),
+    ]);
+
     const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
 
     const headers = Object.entries({
diff --git a/src/client/workflow/models/AnonymizedWorkflowExecution.ts b/src/client/workflow/models/AnonymizedWorkflowExecution.ts
index eb4085fe63360b4b3018cf3e85a097f9679422ed..11497f73bf3bb867a29356784391a47e8b028df0 100644
--- a/src/client/workflow/models/AnonymizedWorkflowExecution.ts
+++ b/src/client/workflow/models/AnonymizedWorkflowExecution.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowExecutionStatus } from './WorkflowExecutionStatus';
-
 export type AnonymizedWorkflowExecution = {
     /**
      * ID of the workflow execution
diff --git a/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts b/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts
index 208b0c24d5b34514fe1156aafeddabd72645a53c..fa8dd9ee4c8d6757fd68c4ede429a0122f03546d 100644
--- a/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts
+++ b/src/client/workflow/models/Body_Workflow_Version_upload_workflow_version_icon.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type Body_Workflow_Version_upload_workflow_version_icon = {
     /**
      * Optional Icon for the Workflow.
diff --git a/src/client/workflow/models/DevWorkflowExecutionIn.ts b/src/client/workflow/models/DevWorkflowExecutionIn.ts
index e8c8a5268a58669f45acee40630ad7d6c1d9c21a..ad6cbd949c0011a656dfd2ef699cfe7e90c180ac 100644
--- a/src/client/workflow/models/DevWorkflowExecutionIn.ts
+++ b/src/client/workflow/models/DevWorkflowExecutionIn.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowModeIn } from './WorkflowModeIn';
-
 export type DevWorkflowExecutionIn = {
     /**
      * Parameters for this workflow
diff --git a/src/client/workflow/models/DocumentationEnum.ts b/src/client/workflow/models/DocumentationEnum.ts
index 1ae04856a01b1b0b6dfe8c550ec97720aaeb4cd6..2f5b3e3d5833fb1775dd5d45ccef997b2e9b471f 100644
--- a/src/client/workflow/models/DocumentationEnum.ts
+++ b/src/client/workflow/models/DocumentationEnum.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export enum DocumentationEnum {
     USAGE = 'usage',
     INPUT = 'input',
diff --git a/src/client/workflow/models/ErrorDetail.ts b/src/client/workflow/models/ErrorDetail.ts
index eba50ab9335b70df9b29cada4c54cf9ffaad10a4..69dd37eb86bc15e2ab2037f3f82516493801e74d 100644
--- a/src/client/workflow/models/ErrorDetail.ts
+++ b/src/client/workflow/models/ErrorDetail.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Schema for a error due to a rejected request.
  */
diff --git a/src/client/workflow/models/HTTPValidationError.ts b/src/client/workflow/models/HTTPValidationError.ts
index c0bcc87cf7f3222638466bc2e5753ca1accf01f7..892e4257ccbb49769737cb2e78e0aad49d81a3b1 100644
--- a/src/client/workflow/models/HTTPValidationError.ts
+++ b/src/client/workflow/models/HTTPValidationError.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { ValidationError } from './ValidationError';
-
 export type HTTPValidationError = {
     detail?: Array<ValidationError>;
 };
diff --git a/src/client/workflow/models/IconUpdateOut.ts b/src/client/workflow/models/IconUpdateOut.ts
index 0499796ef14433d99fae156f3508df5ed0db9984..1210a9304608adaafc41a6be01dbbb145f8d9964 100644
--- a/src/client/workflow/models/IconUpdateOut.ts
+++ b/src/client/workflow/models/IconUpdateOut.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type IconUpdateOut = {
     /**
      * URL to the uploaded icon
diff --git a/src/client/workflow/models/Status.ts b/src/client/workflow/models/Status.ts
index 8da66ea5e286d86b0f4d9d1c3b7bac8167d769ab..fd918bd83e94662808073249cf91cc0d8b721fac 100644
--- a/src/client/workflow/models/Status.ts
+++ b/src/client/workflow/models/Status.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the possible status of a workflow version.
  */
diff --git a/src/client/workflow/models/ValidationError.ts b/src/client/workflow/models/ValidationError.ts
index 18997ec72f4103731f38d915508522ba23ba8506..f2ff49a2b9ad95d18a90c95dc2a7c0b60b7e85e7 100644
--- a/src/client/workflow/models/ValidationError.ts
+++ b/src/client/workflow/models/ValidationError.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type ValidationError = {
     loc: Array<(string | number)>;
     msg: string;
diff --git a/src/client/workflow/models/WorkflowCredentialsIn.ts b/src/client/workflow/models/WorkflowCredentialsIn.ts
index 37723a3e3a9384f04e6d52ecd6ac4f302d6ac304..a3b3c105701dfc4949bd81bb26409275e60a7b64 100644
--- a/src/client/workflow/models/WorkflowCredentialsIn.ts
+++ b/src/client/workflow/models/WorkflowCredentialsIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowCredentialsIn = {
     /**
      * Token to access the content git repository
diff --git a/src/client/workflow/models/WorkflowCredentialsOut.ts b/src/client/workflow/models/WorkflowCredentialsOut.ts
index 8a447b4228303bd6e0204694408a131b9a15b0d2..371252ccef56cbab9fae83919271e08aa6ae198d 100644
--- a/src/client/workflow/models/WorkflowCredentialsOut.ts
+++ b/src/client/workflow/models/WorkflowCredentialsOut.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowCredentialsOut = {
     /**
      * Token to access the content git repository
diff --git a/src/client/workflow/models/WorkflowExecutionIn.ts b/src/client/workflow/models/WorkflowExecutionIn.ts
index 3532aa80d246e34c0974e847894e043dfbc6b02d..223447aee1646e8aa2cb72943f6772a5812e044e 100644
--- a/src/client/workflow/models/WorkflowExecutionIn.ts
+++ b/src/client/workflow/models/WorkflowExecutionIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowExecutionIn = {
     /**
      * Parameters for this workflow
diff --git a/src/client/workflow/models/WorkflowExecutionOut.ts b/src/client/workflow/models/WorkflowExecutionOut.ts
index a804c6ade04581facc1d0543e3fcc60a3a030803..dc3e9811125d2bb768b207dfa0ae0e738a798a32 100644
--- a/src/client/workflow/models/WorkflowExecutionOut.ts
+++ b/src/client/workflow/models/WorkflowExecutionOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowExecutionStatus } from './WorkflowExecutionStatus';
-
 export type WorkflowExecutionOut = {
     /**
      * Workflow version git commit hash
diff --git a/src/client/workflow/models/WorkflowExecutionStatus.ts b/src/client/workflow/models/WorkflowExecutionStatus.ts
index dee8c8fea4f0bbb84f2b8aaf69f626989e1639a6..6b92108d72486d7dbf7cd63606e0e5210d479c02 100644
--- a/src/client/workflow/models/WorkflowExecutionStatus.ts
+++ b/src/client/workflow/models/WorkflowExecutionStatus.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 /**
  * Enumeration for the status on a workflow execution.
  */
diff --git a/src/client/workflow/models/WorkflowIn.ts b/src/client/workflow/models/WorkflowIn.ts
index 9367556e9ffa26da9b64044efbd577d24dc145f7..c5322ce22a5df57eade165e222023333d7d79dd1 100644
--- a/src/client/workflow/models/WorkflowIn.ts
+++ b/src/client/workflow/models/WorkflowIn.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowModeIn } from './WorkflowModeIn';
-
 export type WorkflowIn = {
     /**
      * Short descriptive name of the workflow
diff --git a/src/client/workflow/models/WorkflowModeIn.ts b/src/client/workflow/models/WorkflowModeIn.ts
index a50b038c1abd7c60352b1526e164c652aa99e7df..7d8d2d439cc4fa451a071aa2edbf5457c2c84c06 100644
--- a/src/client/workflow/models/WorkflowModeIn.ts
+++ b/src/client/workflow/models/WorkflowModeIn.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowModeIn = {
     /**
      * Path to the alternative parameter schema
diff --git a/src/client/workflow/models/WorkflowModeOut.ts b/src/client/workflow/models/WorkflowModeOut.ts
index 1d52e08bd2580b58af3b5204a6e6ebc1fa2d6ee8..d3b61d56fadb4e1e93d4ba47a6fb65e2118b6906 100644
--- a/src/client/workflow/models/WorkflowModeOut.ts
+++ b/src/client/workflow/models/WorkflowModeOut.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowModeOut = {
     /**
      * Path to the alternative parameter schema
diff --git a/src/client/workflow/models/WorkflowOut.ts b/src/client/workflow/models/WorkflowOut.ts
index a3d055f5a99d8683b29183cfe42cf01b9903ad28..90fbae386c4790be3b6063d3cff5db722dcd9d4d 100644
--- a/src/client/workflow/models/WorkflowOut.ts
+++ b/src/client/workflow/models/WorkflowOut.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowVersion } from './WorkflowVersion';
-
 export type WorkflowOut = {
     /**
      * Short descriptive name of the workflow
diff --git a/src/client/workflow/models/WorkflowStatistic.ts b/src/client/workflow/models/WorkflowStatistic.ts
index 4b7fbade791196f814a52cee40e8cca117ea8102..d84e2dc9ce8a30ce6d93a355774e56957fb117e3 100644
--- a/src/client/workflow/models/WorkflowStatistic.ts
+++ b/src/client/workflow/models/WorkflowStatistic.ts
@@ -2,7 +2,6 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 export type WorkflowStatistic = {
     /**
      * Day of the datapoint
diff --git a/src/client/workflow/models/WorkflowUpdate.ts b/src/client/workflow/models/WorkflowUpdate.ts
index 6aa6f1874344aff94967d05ddd3958cd86726d71..879f4bc2d303f3e3bd247b63b3996a14b9fa1b41 100644
--- a/src/client/workflow/models/WorkflowUpdate.ts
+++ b/src/client/workflow/models/WorkflowUpdate.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { WorkflowModeIn } from './WorkflowModeIn';
-
 export type WorkflowUpdate = {
     /**
      * Version of the Workflow. Should follow semantic versioning
diff --git a/src/client/workflow/models/WorkflowVersion.ts b/src/client/workflow/models/WorkflowVersion.ts
index 26b656fb2a8edb63630ab92736f2f6c6b592a656..d27d809fcdb2796a2967c04cb429cc9e3528cc86 100644
--- a/src/client/workflow/models/WorkflowVersion.ts
+++ b/src/client/workflow/models/WorkflowVersion.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Status } from './Status';
-
 export type WorkflowVersion = {
     /**
      * Status of the workflow version
diff --git a/src/client/workflow/models/WorkflowVersionStatus.ts b/src/client/workflow/models/WorkflowVersionStatus.ts
index 0c66d1dd6b28229891c5c7d5ceed3eefab291c4e..765e6881cbcd8211096130a094584eb7f04ffa1d 100644
--- a/src/client/workflow/models/WorkflowVersionStatus.ts
+++ b/src/client/workflow/models/WorkflowVersionStatus.ts
@@ -2,9 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
-
 import type { Status } from './Status';
-
 export type WorkflowVersionStatus = {
     /**
      * Status of the workflow version
diff --git a/src/client/workflow/services/WorkflowCredentialsService.ts b/src/client/workflow/services/WorkflowCredentialsService.ts
index 1f9f891efb06fa6cd1b4304e817b59ef954d6cd7..cf305facb9a62fb2441bb3db98d42d53a76a0be4 100644
--- a/src/client/workflow/services/WorkflowCredentialsService.ts
+++ b/src/client/workflow/services/WorkflowCredentialsService.ts
@@ -4,13 +4,10 @@
 /* eslint-disable */
 import type { WorkflowCredentialsIn } from '../models/WorkflowCredentialsIn';
 import type { WorkflowCredentialsOut } from '../models/WorkflowCredentialsOut';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class WorkflowCredentialsService {
-
     /**
      * Get the credentials of a workflow
      * Get the credentials for the repository of a workflow. Only the developer of a workflow can do this.
@@ -37,7 +34,6 @@ export class WorkflowCredentialsService {
             },
         });
     }
-
     /**
      * Update the credentials of a workflow
      * Update the credentials for the repository of a workflow.
@@ -68,7 +64,6 @@ export class WorkflowCredentialsService {
             },
         });
     }
-
     /**
      * Delete the credentials of a workflow
      * Delete the credentials for the repository of a workflow.
@@ -95,5 +90,4 @@ export class WorkflowCredentialsService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/services/WorkflowExecutionService.ts b/src/client/workflow/services/WorkflowExecutionService.ts
index 199ba7b7ac8000a30397d7136a0cc7bec8056c1d..db4e486b8de62e08f85e7d965cc1fb4f667df7b4 100644
--- a/src/client/workflow/services/WorkflowExecutionService.ts
+++ b/src/client/workflow/services/WorkflowExecutionService.ts
@@ -6,13 +6,10 @@ import type { DevWorkflowExecutionIn } from '../models/DevWorkflowExecutionIn';
 import type { WorkflowExecutionIn } from '../models/WorkflowExecutionIn';
 import type { WorkflowExecutionOut } from '../models/WorkflowExecutionOut';
 import type { WorkflowExecutionStatus } from '../models/WorkflowExecutionStatus';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class WorkflowExecutionService {
-
     /**
      * Start a new workflow execution
      * Start a new workflow execution. Workflow versions wit status `DEPRECATED` or `DENIED` can't be started.
@@ -39,7 +36,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Get all workflow executions
      * Get all workflow executions.
@@ -73,7 +69,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Start a workflow execution with arbitrary git repository
      * Start a new workflow execution from an arbitrary git repository.
@@ -103,7 +98,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Get a workflow execution
      * Get a specific workflow execution.
@@ -131,7 +125,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Delete a workflow execution
      * Delete a specific workflow execution.
@@ -159,7 +152,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Get the parameters of a workflow execution
      * Get the parameters of a specific workflow execution.
@@ -187,7 +179,6 @@ export class WorkflowExecutionService {
             },
         });
     }
-
     /**
      * Cancel a workflow execution
      * Cancel a running workflow execution.
@@ -215,5 +206,4 @@ export class WorkflowExecutionService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/services/WorkflowModeService.ts b/src/client/workflow/services/WorkflowModeService.ts
index f5e1926c3ffbe233d8fe9027c91683a71fd23f94..f0fa89dca30c50887a27346f3e647c0fda00c91e 100644
--- a/src/client/workflow/services/WorkflowModeService.ts
+++ b/src/client/workflow/services/WorkflowModeService.ts
@@ -3,13 +3,10 @@
 /* tslint:disable */
 /* eslint-disable */
 import type { WorkflowModeOut } from '../models/WorkflowModeOut';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class WorkflowModeService {
-
     /**
      * Get workflow mode
      * Get a workflow mode
@@ -36,5 +33,4 @@ export class WorkflowModeService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/services/WorkflowService.ts b/src/client/workflow/services/WorkflowService.ts
index b173a102fe754904bcd8acfb703ef02616a7d5e5..9dc2f2ca53a4544e0e67abafcab7c8d6d375730e 100644
--- a/src/client/workflow/services/WorkflowService.ts
+++ b/src/client/workflow/services/WorkflowService.ts
@@ -13,13 +13,10 @@ import type { WorkflowStatistic } from '../models/WorkflowStatistic';
 import type { WorkflowUpdate } from '../models/WorkflowUpdate';
 import type { WorkflowVersion } from '../models/WorkflowVersion';
 import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class WorkflowService {
-
     /**
      * List workflows
      * List all workflows.
@@ -52,7 +49,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Create a new workflow
      * Create a new workflow.
@@ -82,7 +78,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get anonymized workflow execution
      * Get the workflow executions with meta information and anonymized user IDs.
@@ -119,7 +114,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get a workflow
      * Get a specific workflow.
@@ -151,7 +145,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Delete a workflow
      * Delete a workflow.
@@ -178,7 +171,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get statistics for a workflow
      * Get the number of started workflow per day.
@@ -205,7 +197,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Update a workflow
      * Create a new workflow version.
@@ -236,7 +227,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get all versions of a workflow
      * List all versions of a Workflow.
@@ -268,7 +258,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Get a workflow version
      * Get a specific version of a workflow.
@@ -299,7 +288,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Update status of workflow version
      * Update the status of a workflow version.
@@ -333,7 +321,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Deprecate a workflow version
      * Deprecate a workflow version.
@@ -364,7 +351,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Fetch documentation for a workflow version
      * Get the documentation for a specific workflow version.
@@ -403,7 +389,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Upload icon for workflow version
      * Upload an icon for the workflow version and returns the new icon URL.
@@ -437,7 +422,6 @@ export class WorkflowService {
             },
         });
     }
-
     /**
      * Delete icon of workflow version
      * Delete the icon of the workflow version.
@@ -467,5 +451,4 @@ export class WorkflowService {
             },
         });
     }
-
 }
diff --git a/src/client/workflow/services/WorkflowVersionService.ts b/src/client/workflow/services/WorkflowVersionService.ts
index 9553b053cc3ee6f33dd087620f8ad227758324ef..47eb077b1e3a64e7b8e444e74739f4da674e97e4 100644
--- a/src/client/workflow/services/WorkflowVersionService.ts
+++ b/src/client/workflow/services/WorkflowVersionService.ts
@@ -8,13 +8,10 @@ import type { IconUpdateOut } from '../models/IconUpdateOut';
 import type { Status } from '../models/Status';
 import type { WorkflowVersion } from '../models/WorkflowVersion';
 import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus';
-
 import type { CancelablePromise } from '../core/CancelablePromise';
 import { OpenAPI } from '../core/OpenAPI';
 import { request as __request } from '../core/request';
-
 export class WorkflowVersionService {
-
     /**
      * Get all versions of a workflow
      * List all versions of a Workflow.
@@ -46,7 +43,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Get a workflow version
      * Get a specific version of a workflow.
@@ -77,7 +73,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Update status of workflow version
      * Update the status of a workflow version.
@@ -111,7 +106,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Deprecate a workflow version
      * Deprecate a workflow version.
@@ -142,7 +136,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Fetch documentation for a workflow version
      * Get the documentation for a specific workflow version.
@@ -181,7 +174,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Upload icon for workflow version
      * Upload an icon for the workflow version and returns the new icon URL.
@@ -215,7 +207,6 @@ export class WorkflowVersionService {
             },
         });
     }
-
     /**
      * Delete icon of workflow version
      * Delete the icon of the workflow version.
@@ -245,5 +236,4 @@ export class WorkflowVersionService {
             },
         });
     }
-
 }
diff --git a/src/components/BootstrapToast.vue b/src/components/BootstrapToast.vue
new file mode 100644
index 0000000000000000000000000000000000000000..02b92fa3213f8d2e33e508645718abd4a6cadea4
--- /dev/null
+++ b/src/components/BootstrapToast.vue
@@ -0,0 +1,56 @@
+<script setup lang="ts">
+import { useSlots, computed } from "vue";
+
+const slots = useSlots();
+const props = defineProps({
+  colorClass: { type: String, required: false, default: "success" },
+  toastId: { type: String, required: true },
+});
+
+const colorClassName = computed<string>(() => {
+  return "text-bg-" + props.colorClass;
+});
+</script>
+
+<template>
+  <Teleport to="#global-toast-container">
+    <div
+      role="alert"
+      aria-live="assertive"
+      aria-atomic="true"
+      class="toast align-items-center border-0"
+      :class="colorClassName"
+      data-bs-autohide="true"
+      :id="props.toastId"
+    >
+      <div v-if="slots.body" class="toast-header" :class="colorClassName">
+        <div class="me-auto">
+          <slot></slot>
+        </div>
+        <button
+          type="button"
+          class="btn-close btn-close-white me-2 m-auto"
+          data-bs-dismiss="toast"
+          aria-label="Close"
+        ></button>
+      </div>
+      <div v-else class="d-flex">
+        <div class="toast-body">
+          <slot></slot>
+        </div>
+        <button
+          type="button"
+          class="btn-close btn-close-white me-2 m-auto"
+          data-bs-dismiss="toast"
+          aria-label="Close"
+        ></button>
+      </div>
+
+      <div class="toast-body" v-if="slots.body">
+        <slot name="body"></slot>
+      </div>
+    </div>
+  </Teleport>
+</template>
+
+<style scoped></style>
diff --git a/src/components/CopyToClipboardIcon.vue b/src/components/CopyToClipboardIcon.vue
index 9d28cbaac691f1c1a6f8098a4e48396144561203..b0d34b4967cf27383093ed940df08e07f934be79 100644
--- a/src/components/CopyToClipboardIcon.vue
+++ b/src/components/CopyToClipboardIcon.vue
@@ -2,9 +2,11 @@
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { onMounted } from "vue";
 import { Toast, Tooltip } from "bootstrap";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const props = defineProps<{
   text: string;
+  button?: boolean;
 }>();
 
 let successToast: Toast | null = null;
@@ -26,52 +28,28 @@ function copyToClipboard() {
 }
 
 onMounted(() => {
-  new Tooltip("#tooltip-" + randomIDSuffix);
+  if (!props.button) {
+    new Tooltip("#tooltip-" + randomIDSuffix);
+  }
   successToast = new Toast("#successToast-" + randomIDSuffix);
   failToast = new Toast("#failToast-" + randomIDSuffix);
 });
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">Successfully copied to clipboard</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-danger align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'failToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">Can't copy to clipboard</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix"
+    >Successfully copied to clipboard
+  </bootstrap-toast>
+  <bootstrap-toast
+    :toast-id="'failToast-' + randomIDSuffix"
+    color-class="danger"
+    >Can't copy to clipboard
+  </bootstrap-toast>
+  <button v-if="props.button" @click="copyToClipboard" class="btn btn-primary">
+    Copy to Clipboard
+  </button>
   <span
+    v-else
     class="hover-info cursor-pointer"
     data-bs-toggle="tooltip"
     data-bs-title="Copy to Clipboard"
@@ -81,8 +59,4 @@ onMounted(() => {
   </span>
 </template>
 
-<style scoped>
-.hover-info:hover {
-  color: var(--bs-info) !important;
-}
-</style>
+<style scoped></style>
diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue
index c3bb9a44c8437cf3c8c845f3cae3b00b5e478bd3..37a3540c874d27e715d88a1b9e3b8e32cb2acbf1 100644
--- a/src/components/NavbarTop.vue
+++ b/src/components/NavbarTop.vue
@@ -8,6 +8,7 @@ import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import { OpenAPI as S3ProxyOpenAPI } from "@/client/s3proxy";
 import { OpenAPI as AuthOpenAPI } from "@/client/auth";
 import { OpenAPI as WorkflowOpenAPI } from "@/client/workflow";
+import { OpenAPI as ResourceOpenAPI } from "@/client/resource";
 import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue";
 import dayjs from "dayjs";
 
@@ -27,6 +28,8 @@ const objectStorageActive = computed<boolean>(
 const workflowActive = computed<boolean>(
   () => activeRoute.value == "workflows",
 );
+const resourceActive = computed<boolean>(() => activeRoute.value == "resource");
+const adminActive = computed<boolean>(() => activeRoute.value == "admin");
 
 watch(
   () => route.name,
@@ -36,6 +39,10 @@ watch(
         activeRoute.value = "buckets";
       } else if (to.startsWith("workflow")) {
         activeRoute.value = "workflows";
+      } else if (to.startsWith("resource")) {
+        activeRoute.value = "resource";
+      } else if (to.startsWith("admin")) {
+        activeRoute.value = "admin";
       } else {
         activeRoute.value = to;
       }
@@ -48,7 +55,7 @@ watch(
 
 <template>
   <header
-    class="navbar navbar-expand-lg bd-navbar sticky-top border-bottom border-secondary"
+    class="navbar navbar-expand bd-navbar sticky-top border-bottom border-secondary"
   >
     <nav class="container-xxl bd-gutter flex-wrap flex-lg-nowrap text-light">
       <router-link
@@ -65,7 +72,7 @@ watch(
         CloWM
       </router-link>
 
-      <div class="d-flex me-auto w-fit" v-if="store.authenticated">
+      <div class="d-flex flex-grow-1" v-if="store.authenticated">
         <ul class="navbar-nav">
           <li class="nav-item dropdown">
             <a
@@ -125,11 +132,7 @@ watch(
                   >My Workflow Executions
                 </router-link>
               </li>
-              <li
-                v-if="
-                  store.workflowDev || store.workflowReviewer || store.admin
-                "
-              >
+              <li v-if="store.workflowDev || store.rewiewer || store.admin">
                 <hr class="dropdown-divider" />
               </li>
               <li v-if="store.workflowDev || store.admin">
@@ -139,7 +142,7 @@ watch(
                   >My Workflows
                 </router-link>
               </li>
-              <li v-if="store.workflowReviewer || store.admin">
+              <li v-if="store.rewiewer || store.admin">
                 <router-link
                   class="dropdown-item"
                   :to="{ name: 'workflows-reviewer' }"
@@ -148,6 +151,78 @@ watch(
               </li>
             </ul>
           </li>
+          <li class="nav-item dropdown">
+            <a
+              class="nav-link dropdown-toggle"
+              :class="{ 'text-black': resourceActive }"
+              id="resourceDropdown"
+              href="#"
+              role="button"
+              data-bs-toggle="dropdown"
+              aria-expanded="false"
+              data-bs-auto-close="true"
+            >
+              Resources
+            </a>
+            <ul
+              class="dropdown-menu shadow m-0"
+              aria-labelledby="workflowDropdown"
+            >
+              <li>
+                <router-link class="dropdown-item" :to="{ name: 'resources' }"
+                  >Available Resources
+                </router-link>
+              </li>
+              <li
+                v-if="store.resourceMaintainer || store.rewiewer || store.admin"
+              >
+                <hr class="dropdown-divider" />
+              </li>
+              <li v-if="store.resourceMaintainer || store.admin">
+                <router-link
+                  class="dropdown-item"
+                  :to="{ name: 'resource-maintainer' }"
+                  >My Resources
+                </router-link>
+              </li>
+              <li v-if="store.rewiewer || store.admin">
+                <router-link
+                  class="dropdown-item"
+                  :to="{ name: 'resource-review' }"
+                  >Review
+                </router-link>
+              </li>
+            </ul>
+          </li>
+          <li v-if="store.admin">
+            <a
+              class="nav-link dropdown-toggle"
+              :class="{ 'text-black': adminActive }"
+              id="adminDropdown"
+              href="#"
+              role="button"
+              data-bs-toggle="dropdown"
+              aria-expanded="false"
+              data-bs-auto-close="true"
+            >
+              Admin
+            </a>
+            <ul
+              class="dropdown-menu shadow m-0"
+              aria-labelledby="adminDropdown"
+            >
+              <li><a class="dropdown-item disabled" href="#">User</a></li>
+              <li><a class="dropdown-item disabled" href="#">Bucket</a></li>
+              <li><a class="dropdown-item disabled" href="#">Workflow</a></li>
+              <li>
+                <router-link
+                  class="dropdown-item"
+                  :to="{ name: 'admin-resources' }"
+                  >Resources
+                </router-link>
+              </li>
+            </ul>
+          </li>
         </ul>
       </div>
       <div class="dropdown" v-if="store.authenticated && store.user != null">
@@ -158,7 +233,7 @@ watch(
           data-bs-toggle="dropdown"
           aria-expanded="false"
         >
-          <strong class="me-2">{{ store.user.display_name }}</strong>
+          <strong class="me-2">{{ store.user?.display_name }}</strong>
           <font-awesome-icon icon="fa-solid fa-circle-user" class="fs-5" />
         </a>
         <ul
@@ -202,10 +277,10 @@ watch(
     </nav>
   </header>
   <bootstrap-modal
-    static-backdrop
-    modal-i-d="advancedUsageModal"
+    modal-id="advancedUsageModal"
     modal-label="Advanced Usage Modal"
     v-if="store.authenticated"
+    size-modifier="lg"
   >
     <template v-slot:header>
       <h3>Advanced Usage</h3>
@@ -249,6 +324,18 @@ watch(
               >
             </td>
           </tr>
+          <tr>
+            <td class="text-end">Resource Service:</td>
+            <td>
+              <a :href="ResourceOpenAPI.BASE + '/docs'" target="_blank">
+                <font-awesome-icon
+                  class="me-1"
+                  icon="fa-solid fa-arrow-up-right-from-square"
+                />
+                {{ ResourceOpenAPI.BASE }}</a
+              >
+            </td>
+          </tr>
         </tbody>
       </table>
       <div class="mt-4">
diff --git a/src/components/modals/BootstrapModal.vue b/src/components/modals/BootstrapModal.vue
index 4e6fd34d52d553fe70460bc048f0d9461572f383..bb4940543ce0dbab9669e28f299d0a443ab74989 100644
--- a/src/components/modals/BootstrapModal.vue
+++ b/src/components/modals/BootstrapModal.vue
@@ -1,15 +1,25 @@
 <script setup lang="ts">
-defineProps<{
-  modalID: string;
+import { computed } from "vue";
+
+const props = defineProps<{
+  modalId: string;
   modalLabel: string;
-  staticBackdrop: boolean;
+  staticBackdrop?: boolean;
+  sizeModifier?: string; // https://getbootstrap.com/docs/5.3/components/modal/#optional-sizes, e.g. sm, lg and xl
 }>();
+
+const modalSizeClass = computed<string>(() => {
+  if (props.sizeModifier == undefined) {
+    return "";
+  }
+  return "modal-" + props.sizeModifier;
+});
 </script>
 
 <template>
   <div
     class="modal"
-    :id="modalID"
+    :id="modalId"
     tabindex="-1"
     :aria-labelledby="modalLabel"
     aria-hidden="true"
@@ -17,7 +27,7 @@ defineProps<{
   >
     <div
       class="modal-dialog modal-dialog-centered modal-dialog-scrollable"
-      style="min-width: 30%"
+      :class="[modalSizeClass]"
     >
       <div class="modal-content">
         <div class="modal-header">
@@ -38,7 +48,15 @@ defineProps<{
           <slot name="body" />
         </div>
         <div class="modal-footer">
-          <slot name="footer" />
+          <slot name="footer">
+            <button
+              type="button"
+              class="btn btn-secondary"
+              data-bs-dismiss="modal"
+            >
+              Close
+            </button>
+          </slot>
         </div>
       </div>
     </div>
diff --git a/src/components/modals/DeleteModal.vue b/src/components/modals/DeleteModal.vue
index b51c55f02d5f6c357fc758f0d5311cc453eaa30c..0a7c662f06a0e2e73e43cf3d81b8719d846c54ed 100644
--- a/src/components/modals/DeleteModal.vue
+++ b/src/components/modals/DeleteModal.vue
@@ -34,7 +34,7 @@ onMounted(() => {
 
 <template>
   <bootstrap-modal
-    :modalID="props.modalID"
+    :modalId="props.modalID"
     :static-backdrop="true"
     modal-label="Confirm Delete Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
diff --git a/src/components/modals/SearchUserModal.vue b/src/components/modals/SearchUserModal.vue
index 85f24c3167c77a967f8288b128ef081f0499e235..414369508f1be90c59a4dab83446ddddb89450b7 100644
--- a/src/components/modals/SearchUserModal.vue
+++ b/src/components/modals/SearchUserModal.vue
@@ -1,18 +1,22 @@
 <script setup lang="ts">
-import { reactive, watch } from "vue";
+import { reactive, ref, watch } from "vue";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { UserService } from "@/client/auth";
 import type { User } from "@/client/auth";
 import { useAuthStore } from "@/stores/users";
+import { useNameStore } from "@/stores/names";
 
 const props = defineProps<{
-  modalID: string;
+  modalId: string;
   backModalId?: string;
+  filterUserSelf?: boolean;
 }>();
 const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 
 const store = useAuthStore();
+const nameStore = useNameStore();
+const textInputElement = ref<HTMLInputElement | undefined>(undefined);
 
 const formState = reactive<{
   searchString: string;
@@ -53,18 +57,24 @@ function modalClosed() {
   formState.potentialUsers = [];
 }
 
+function modalShown() {
+  textInputElement.value?.focus();
+}
+
 function searchUser(name: string) {
   formState.error = false;
   UserService.userListUsers(name)
     .then((userSuggestions) => {
-      formState.potentialUsers = userSuggestions.filter(
-        (user) => store.currentUID != user.uid,
-      );
-      const uidToName: Record<string, string> = {};
+      if (props.filterUserSelf) {
+        formState.potentialUsers = userSuggestions.filter(
+          (user) => store.currentUID != user.uid,
+        );
+      } else {
+        formState.potentialUsers = userSuggestions;
+      }
       for (const user of userSuggestions) {
-        uidToName[user.uid] = user.display_name;
+        nameStore.addNameToMapping(user.uid, user.display_name);
       }
-      store.addUidToNameMapping(uidToName);
     })
     .catch((err) => {
       formState.error = true;
@@ -78,10 +88,10 @@ function searchUser(name: string) {
 
 <template>
   <bootstrap-modal
-    :modalID="props.modalID"
+    :modalId="props.modalId"
     :static-backdrop="true"
     modal-label="Search User Modal"
-    v-on="{ 'hidden.bs.modal': modalClosed }"
+    v-on="{ 'hidden.bs.modal': modalClosed, 'shown.bs.modal': modalShown }"
   >
     <template v-slot:header>Search User</template>
     <template v-slot:body>
@@ -94,6 +104,7 @@ function searchUser(name: string) {
           :id="'searchUserInput' + randomIDSuffix"
           placeholder="Search for a user"
           v-model.trim="formState.searchString"
+          ref="textInputElement"
         />
       </div>
       <div v-if="formState.loading" class="text-center">
@@ -106,7 +117,8 @@ function searchUser(name: string) {
           icon="fa-solid fa-x"
           class="mb-2"
           style="color: var(--bs-danger); font-size: 4em"
-        /><br />
+        />
+        <br />
         <span class="text-danger"
           >There seems to be an error<br />Try again later</span
         >
@@ -117,8 +129,9 @@ function searchUser(name: string) {
           :key="user.uid"
           type="button"
           class="list-group-item list-group-item-action"
-          :data-bs-target="'#' + props.backModalId"
-          data-bs-toggle="modal"
+          :data-bs-target="props.backModalId ? '#' + props.backModalId : null"
+          :data-bs-toggle="props.backModalId ? 'modal' : null"
+          :data-bs-dismiss="props.backModalId ? null : 'modal'"
           @click="emit('user-found', user)"
         >
           {{ user.display_name }}
@@ -129,7 +142,8 @@ function searchUser(name: string) {
           icon="fa-solid fa-magnifying-glass"
           class="mb-2"
           style="color: var(--bs-secondary); font-size: 4em"
-        /><br />
+        />
+        <br />
         <span v-if="formState.searchString.length > 2"
           >Could not find any Users</span
         >
diff --git a/src/components/object-storage/BucketListItem.vue b/src/components/object-storage/BucketListItem.vue
index a1db0ae77fa2f80099523ab93c1dc26b94ccb542..04f5f4462fd8f1ff7f677e039a0a101b30ab6f81 100644
--- a/src/components/object-storage/BucketListItem.vue
+++ b/src/components/object-storage/BucketListItem.vue
@@ -12,6 +12,7 @@ import { useBucketStore } from "@/stores/buckets";
 import { useRouter } from "vue-router";
 import { useAuthStore } from "@/stores/users";
 import type { FolderTree } from "@/types/PseudoFolder";
+import { useNameStore } from "@/stores/names";
 
 const props = defineProps<{
   active: boolean;
@@ -23,6 +24,7 @@ const props = defineProps<{
 const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 const permissionRepository = useBucketStore();
 const userRepository = useAuthStore();
+const nameRepository = useNameStore();
 const router = useRouter();
 
 const permission = computed<BucketPermissionOut | undefined>(
@@ -168,7 +170,7 @@ onMounted(() => {
             <tr v-if="permission">
               <th scope="row" class="fw-bold">Owner:</th>
               <td class="text-truncate">
-                {{ userRepository.userMapping[bucket.owner] }}
+                {{ nameRepository.getName(bucket.owner) }}
               </td>
             </tr>
             <tr
diff --git a/src/components/object-storage/modals/BucketDetailModal.vue b/src/components/object-storage/modals/BucketDetailModal.vue
index 09788df9d94e1e290c501bc98cc7e53aadbf1bd5..49eef5918ab7e1e2f26d7a49e876d9a95f386be1 100644
--- a/src/components/object-storage/modals/BucketDetailModal.vue
+++ b/src/components/object-storage/modals/BucketDetailModal.vue
@@ -12,7 +12,7 @@ const props = defineProps<{
 
 <template>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="false"
     modal-label="Bucket Detail Modal"
   >
diff --git a/src/components/object-storage/modals/CopyObjectModal.vue b/src/components/object-storage/modals/CopyObjectModal.vue
index 0687aaa6a9ceee3c848e94eac1a588108848a6fb..68f8b3d1a386ca24b0b712836b728e7eeaec85d1 100644
--- a/src/components/object-storage/modals/CopyObjectModal.vue
+++ b/src/components/object-storage/modals/CopyObjectModal.vue
@@ -5,6 +5,7 @@ import { onMounted, reactive, watch } from "vue";
 import type { _Object as S3Object } from "@aws-sdk/client-s3";
 import { useBucketStore } from "@/stores/buckets";
 import { useS3ObjectStore } from "@/stores/s3objects";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const objectRepository = useS3ObjectStore();
 
@@ -83,51 +84,18 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">Successfully copied file</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-danger align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'errorToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">
-          There has been some Error.<br />
-          Try again later
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix">
+    Successfully copied file
+  </bootstrap-toast>
+  <bootstrap-toast
+    :toast-id="'errorToast-' + randomIDSuffix"
+    color-class="danger"
+  >
+    There has been some Error.<br />
+    Try again later
+  </bootstrap-toast>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Copy Object Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
diff --git a/src/components/object-storage/modals/CreateBucketModal.vue b/src/components/object-storage/modals/CreateBucketModal.vue
index ae030960e3ab025834d85ba26ef3f56a17d297a4..9de88c11559748cd7fc22d701c42ef294b879af9 100644
--- a/src/components/object-storage/modals/CreateBucketModal.vue
+++ b/src/components/object-storage/modals/CreateBucketModal.vue
@@ -82,7 +82,7 @@ function modalClosed() {
 
 <template>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Create Bucket Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
diff --git a/src/components/object-storage/modals/CreateFolderModal.vue b/src/components/object-storage/modals/CreateFolderModal.vue
index cd26d2c2a94ff03d6abe9a68e0f2fb5e623a081a..7791c9c7a62de44f7b7e290a7be4d0b2fbd9c76d 100644
--- a/src/components/object-storage/modals/CreateFolderModal.vue
+++ b/src/components/object-storage/modals/CreateFolderModal.vue
@@ -4,6 +4,7 @@ import { computed, onMounted, reactive } from "vue";
 
 import { Modal, Toast } from "bootstrap";
 import { useS3ObjectStore } from "@/stores/s3objects";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const objectRepository = useS3ObjectStore();
 
@@ -67,51 +68,18 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">Successfully created Folder</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-danger align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'errorToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">
-          There has been some Error.<br />
-          Try again later
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix">
+    Successfully created Folder
+  </bootstrap-toast>
+  <bootstrap-toast
+    :toast-id="'errorToast-' + randomIDSuffix"
+    color-class="danger"
+  >
+    There has been some Error.<br />
+    Try again later
+  </bootstrap-toast>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Create Folder Modal"
   >
diff --git a/src/components/object-storage/modals/ObjectDetailModal.vue b/src/components/object-storage/modals/ObjectDetailModal.vue
index 08f5280a86aac2400e20909bae8723377ac92916..c1c2eaf0b1f2006db22acb8f893daf221af573c8 100644
--- a/src/components/object-storage/modals/ObjectDetailModal.vue
+++ b/src/components/object-storage/modals/ObjectDetailModal.vue
@@ -53,7 +53,7 @@ onMounted(() => {
 
 <template>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="false"
     modal-label="Object Detail Modal"
   >
diff --git a/src/components/object-storage/modals/PermissionListModal.vue b/src/components/object-storage/modals/PermissionListModal.vue
index 1d5c4d020a74e2bf737026c03ad5610cb095b41e..8d74a24c67c91f2a10dfa910c6f522e94c4c1cf6 100644
--- a/src/components/object-storage/modals/PermissionListModal.vue
+++ b/src/components/object-storage/modals/PermissionListModal.vue
@@ -6,8 +6,13 @@ import { onBeforeMount, watch } from "vue";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import PermissionModal from "@/components/object-storage/modals/PermissionModal.vue";
 import { useBucketStore } from "@/stores/buckets";
+import { useNameStore } from "@/stores/names";
+import { useAuthStore } from "@/stores/users";
 
 const bucketRepository = useBucketStore();
+const nameRepository = useNameStore();
+const userRepository = useAuthStore();
+
 // Props
 // -----------------------------------------------------------------------------
 const props = defineProps<{
@@ -46,7 +51,9 @@ watch(
 // Function
 // -----------------------------------------------------------------------------
 function updateBucketPermissions(bucketName: string) {
-  bucketRepository.fetchBucketPermissions(bucketName);
+  bucketRepository.fetchBucketPermissions(bucketName).then((permissions) => {
+    userRepository.fetchUsernames(permissions.map((p) => p.uid));
+  });
 }
 
 // Lifecycle Hooks
@@ -68,7 +75,7 @@ onBeforeMount(() => {
     :modalID="'permission-list-edit-modal' + randomIDSuffix"
   />
   <bootstrap-modal
-    :modalID="props.modalID"
+    :modalId="props.modalID"
     :static-backdrop="true"
     modal-label="Permission List Modal"
   >
@@ -99,7 +106,7 @@ onBeforeMount(() => {
                 permission.permission
               }}</span>
               <span class="col-9 text-center">
-                {{ permission.grantee_display_name }}</span
+                {{ nameRepository.getName(permission.uid) }}</span
               >
             </div>
           </button>
diff --git a/src/components/object-storage/modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue
index 60ad031740571fd138c8cb918a68137d4bf94259..846aad4522e2f640ef46e01b4f6eec3086a798df 100644
--- a/src/components/object-storage/modals/PermissionModal.vue
+++ b/src/components/object-storage/modals/PermissionModal.vue
@@ -16,6 +16,8 @@ import { Permission } from "@/client/s3proxy";
 import { Toast } from "bootstrap";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { useBucketStore } from "@/stores/buckets";
+import BootstrapToast from "@/components/BootstrapToast.vue";
+import { useNameStore } from "@/stores/names";
 
 // Props
 // -----------------------------------------------------------------------------
@@ -31,6 +33,7 @@ const props = defineProps<{
 }>();
 
 const bucketRepository = useBucketStore();
+const nameRepository = useNameStore();
 
 const emit = defineEmits<{ (e: "permission-deleted"): void }>();
 // Variables
@@ -44,12 +47,10 @@ let successToast: Toast | null = null;
 // eslint-disable-next-line vue/no-setup-props-destructure
 const formState = reactive<{
   loading: boolean;
-  grantee_name: string;
   error: boolean;
   readonly: boolean;
 }>({
   loading: false,
-  grantee_name: "",
   error: false,
   readonly: props.readonly,
 });
@@ -132,7 +133,6 @@ function updateLocalPermission() {
     permission.bucket_name = props.editUserPermission.bucket_name;
     permission.file_prefix = props.editUserPermission.file_prefix;
     permission.uid = props.editUserPermission.uid;
-    formState.grantee_name = props.editUserPermission.grantee_display_name;
     permission.from_timestamp = props.editUserPermission.from_timestamp;
     permission.to_timestamp = props.editUserPermission.to_timestamp;
     permission.permission = props.editUserPermission.permission;
@@ -142,7 +142,6 @@ function updateLocalPermission() {
     permission.from_timestamp = undefined;
     permission.to_timestamp = undefined;
     permission.permission = undefined;
-    formState.grantee_name = "";
   }
 }
 
@@ -233,7 +232,6 @@ function confirmedDeletePermission(bucketName: string, uid: string) {
 
 function updateUser(user: User) {
   permission.uid = user.uid;
-  formState.grantee_name = user.display_name;
 }
 
 // Lifecycle Hooks
@@ -266,40 +264,24 @@ function toTimestampChanged(target?: HTMLInputElement | null) {
       confirmedDeletePermission(permission.bucket_name, permission.uid)
     "
   />
-  <SearchUserModal
-    :modalID="'search-user-modal' + randomIDSuffix"
+  <search-user-modal
+    :modal-id="'search-user-modal' + randomIDSuffix"
     :back-modal-id="modalID"
+    filter-user-self
     @user-found="updateUser"
   />
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'toast-' + randomIDSuffix"
-      v-on="{ 'hidden.bs.toast': toastHidden }"
-    >
-      <div class="d-flex">
-        <div class="toast-body">
-          Successfully
-          <span v-if="permissionDeleted">deleted</span>
-          <span v-else-if="editPermission">edited</span>
-          <span v-else>created</span>
-          Permission
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast
+    :toast-id="'toast-' + randomIDSuffix"
+    v-on="{ 'hidden.bs.toast': toastHidden }"
+  >
+    Successfully
+    <template v-if="permissionDeleted">deleted</template>
+    <template v-else-if="editPermission">edited</template>
+    <template v-else>created</template>
+    Permission
+  </bootstrap-toast>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Permission Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
@@ -352,32 +334,23 @@ function toTimestampChanged(target?: HTMLInputElement | null) {
           <label for="permissionGranteeInput" class="col-2 col-form-label">
             User<span v-if="!formState.readonly">*</span>
           </label>
-          <div
-            :class="{
-              'col-10': permissionUserReadonly,
-              'col-9': !permissionUserReadonly,
-            }"
-          >
+          <div class="col-10">
             <input
               type="text"
               class="form-control"
               id="permissionGranteeInput"
               required
               placeholder="Search for a user"
-              v-model.trim="formState.grantee_name"
+              :value="nameRepository.getName(permission.uid)"
               readonly
+              :data-bs-toggle="permissionUserReadonly ? null : 'modal'"
+              :data-bs-target="
+                permissionUserReadonly
+                  ? null
+                  : '#search-user-modal' + randomIDSuffix
+              "
             />
           </div>
-          <div v-if="!permissionUserReadonly" class="col-1">
-            <button
-              type="button"
-              class="btn btn-secondary btn-sm float-end"
-              data-bs-toggle="modal"
-              :data-bs-target="'#search-user-modal' + randomIDSuffix"
-            >
-              <font-awesome-icon icon="fa-solid fa-magnifying-glass" />
-            </button>
-          </div>
         </div>
         <div class="mb-3 row">
           <label for="permissionTypeInput" class="col-3 col-form-label">
diff --git a/src/components/object-storage/modals/UploadObjectModal.vue b/src/components/object-storage/modals/UploadObjectModal.vue
index adafe9a20af1735e997005d1737f9b3dc2edaa1b..331fbf62a255527b1e1220c4d4b9db089eb554c6 100644
--- a/src/components/object-storage/modals/UploadObjectModal.vue
+++ b/src/components/object-storage/modals/UploadObjectModal.vue
@@ -4,6 +4,7 @@ import { computed, onMounted, reactive, ref, watch } from "vue";
 import { Modal, Toast } from "bootstrap";
 import { partial } from "filesize";
 import { useS3ObjectStore } from "@/stores/s3objects";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const fsize = partial({ base: 2, standard: "jedec" });
 const objectRepository = useS3ObjectStore();
@@ -101,51 +102,18 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">Successfully uploaded file</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-danger align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'errorToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">
-          There has been some Error.<br />
-          Try again later
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix">
+    Successfully uploaded file
+  </bootstrap-toast>
+  <bootstrap-toast
+    :toast-id="'errorToast-' + randomIDSuffix"
+    color-class="danger"
+  >
+    There has been some Error.<br />
+    Try again later
+  </bootstrap-toast>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Upload Object Modal"
   >
@@ -221,15 +189,17 @@ onMounted(() => {
     </template>
     <template v-slot:footer>
       <div class="w-50 me-auto" v-if="formState.uploading">
-        <div class="progress">
+        <div
+          class="progress"
+          aria-valuemin="0"
+          aria-valuemax="100"
+          role="progressbar"
+          :aria-valuenow="uploadProgress"
+          aria-label="Upload Progressbar"
+        >
           <div
             class="progress-bar bg-info"
-            role="progressbar"
-            aria-label="Basic example"
             :style="{ width: uploadProgress + '%' }"
-            :aria-valuenow="uploadProgress"
-            aria-valuemin="0"
-            aria-valuemax="100"
           >
             {{ uploadProgress }}%
           </div>
diff --git a/src/components/parameter-schema/ParameterSchemaFormComponent.vue b/src/components/parameter-schema/ParameterSchemaFormComponent.vue
index 7546e8bbb109e223955dd0c548f8ca097799c33d..aed548155d295a5a6e57765799f9626a3661e422 100644
--- a/src/components/parameter-schema/ParameterSchemaFormComponent.vue
+++ b/src/components/parameter-schema/ParameterSchemaFormComponent.vue
@@ -8,9 +8,12 @@ import ParameterStringInput from "@/components/parameter-schema/form-mode/Parame
 import { Toast } from "bootstrap";
 import { useBucketStore } from "@/stores/buckets";
 import { useS3KeyStore } from "@/stores/s3keys";
+import BootstrapToast from "@/components/BootstrapToast.vue";
+import { useResourceStore } from "@/stores/resources";
 
 const bucketRepository = useBucketStore();
-const s3KeyRepository = useS3KeyStore();
+const resourceRepository = useResourceStore();
+const keyRepository = useS3KeyStore();
 
 // Props
 // =============================================================================
@@ -180,40 +183,25 @@ onMounted(() => {
   if (props.schema) updateSchema(props.schema);
   bucketRepository.fetchBuckets();
   bucketRepository.fetchOwnPermissions();
-  s3KeyRepository.fetchS3Keys();
+  keyRepository.fetchS3Keys();
+  resourceRepository.fetchPublicResources();
   errorToast = new Toast("#workflowExecutionErrorToast");
 });
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-danger align-items-center border-0"
-      data-bs-autohide="true"
-      id="workflowExecutionErrorToast"
-    >
-      <div class="d-flex p-2 justify-content-between align-items-center">
-        <div class="toast-body">
-          <template v-if="formState.errorType === 'form'">
-            Some inputs are not valid.
-          </template>
-          <template v-else>
-            There was an error with starting the workflow execution. Look in the
-            console for more information.
-          </template>
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast toast-id="workflowExecutionErrorToast" color-class="danger">
+    <template>Error starting workflow</template>
+    <template #body>
+      <template v-if="formState.errorType === 'form'">
+        Some inputs are not valid.
+      </template>
+      <template v-else>
+        There was an error with starting the workflow execution. Look in the
+        console for more information.
+      </template>
+    </template>
+  </bootstrap-toast>
   <div class="row mb-5 align-items-start">
     <form
       v-if="props.schema"
@@ -245,14 +233,11 @@ onMounted(() => {
           <h5 class="card-title">
             General Options about the pipeline execution
           </h5>
-          <template v-if="props.allowNotes">
+          <div v-if="props.allowNotes">
+            <code class="bg-secondary-subtle p-2 rounded-top">--notes</code>
             <div class="input-group">
               <span class="input-group-text" id="pipelineNotes">
-                <font-awesome-icon
-                  class="me-2"
-                  icon="fa-solid fa-sticky-note"
-                />
-                <code>--notes</code>
+                <font-awesome-icon icon="fa-solid fa-sticky-note" />
               </span>
               <textarea
                 class="form-control"
@@ -263,60 +248,75 @@ onMounted(() => {
             <label class="mb-3" for="pipelineNotes"
               >Personal notes about the pipeline execution</label
             >
-          </template>
-          <div class="input-group">
-            <span class="input-group-text" id="logsS3Path">
-              <font-awesome-icon class="me-2" icon="fa-solid fa-folder" />
-              <code>--logs_s3_path</code>
-            </span>
-            <parameter-string-input
-              parameter-name="logs_s3_path"
-              v-model="formState.logs_s3_path"
-              :parameter="{
-                format: 'directory-path',
-                type: 'string',
-              }"
-            />
           </div>
-          <label class="mb-3" for="logsS3Path">
-            Directory in bucket where to save Nextflow log and reports
-          </label>
-          <div class="input-group">
-            <span class="input-group-text" id="provenanceS3Path">
-              <font-awesome-icon class="me-2" icon="fa-solid fa-folder" />
-              <code>--provenance_s3_path</code>
-            </span>
-            <parameter-string-input
-              parameter-name="provenance_s3_path"
-              v-model="formState.provenance_s3_path"
-              :parameter="{
-                format: 'directory-path',
-                type: 'string',
-              }"
-            />
+          <div>
+            <code class="bg-secondary-subtle p-2 rounded-top"
+              >--logs_s3_path</code
+            >
+            <div class="input-group">
+              <span class="input-group-text">
+                <font-awesome-icon icon="fa-solid fa-folder" />
+              </span>
+              <parameter-string-input
+                parameter-name="logs_s3_path"
+                v-model="formState.logs_s3_path"
+                :parameter="{
+                  format: 'directory-path',
+                  type: 'string',
+                }"
+                remove-advanced
+              />
+            </div>
+            <label class="mb-3" for="logsS3Path">
+              Directory in bucket where to save Nextflow log and reports
+            </label>
           </div>
-          <label class="mb-3" for="provenanceS3Path">
-            Directory in bucket where to save provenance information about the
-            workflow execution
-          </label>
-          <div class="input-group">
-            <span class="input-group-text" id="debugS3Path">
-              <font-awesome-icon class="me-2" icon="fa-solid fa-folder" />
-              <code>--debug_s3_path</code>
-            </span>
-            <parameter-string-input
-              parameter-name="debug_s3_path"
-              v-model="formState.debug_s3_path"
-              :parameter="{
-                format: 'directory-path',
-                type: 'string',
-              }"
-            />
+          <div>
+            <code class="bg-secondary-subtle p-2 rounded-top"
+              >--provenance_s3_path</code
+            >
+            <div class="input-group">
+              <span class="input-group-text">
+                <font-awesome-icon icon="fa-solid fa-folder" />
+              </span>
+              <parameter-string-input
+                parameter-name="provenance_s3_path"
+                v-model="formState.provenance_s3_path"
+                :parameter="{
+                  format: 'directory-path',
+                  type: 'string',
+                }"
+                remove-advanced
+              />
+            </div>
+            <label class="mb-3" for="provenanceS3Path">
+              Directory in bucket where to save provenance information about the
+              workflow execution
+            </label>
+          </div>
+          <div>
+            <code class="bg-secondary-subtle p-2 rounded-top"
+              >--debug_s3_path</code
+            >
+            <div class="input-group">
+              <span class="input-group-text">
+                <font-awesome-icon icon="fa-solid fa-folder" />
+              </span>
+              <parameter-string-input
+                parameter-name="debug_s3_path"
+                v-model="formState.debug_s3_path"
+                :parameter="{
+                  format: 'directory-path',
+                  type: 'string',
+                }"
+                remove-advanced
+              />
+            </div>
+            <label class="mb-3" for="debugS3Path">
+              Directory in bucket where to save debug information about the
+              workflow execution
+            </label>
           </div>
-          <label class="mb-3" for="debugS3Path">
-            Directory in bucket where to save debug information about the
-            workflow execution
-          </label>
         </div>
       </div>
     </form>
diff --git a/src/components/parameter-schema/form-mode/ParameterGroupForm.vue b/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
index e66ef66d0dfdaaa8468d303dd18325a22e7fafe5..7789da50ea7c040b399271b5d35d7cf5706a9fff 100644
--- a/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
+++ b/src/components/parameter-schema/form-mode/ParameterGroupForm.vue
@@ -65,7 +65,6 @@ function parameterRequired(
 watch(
   formInput,
   (newVal) => {
-    //console.log("Group", props.parameterGroupName, newVal);
     emit("update:modelValue", newVal);
   },
   {
@@ -87,14 +86,12 @@ watch(
         :key="parameterName"
       >
         <div :hidden="!showHidden && parameter['hidden']">
+          <code class="bg-secondary-subtle p-2 rounded-top"
+            >--{{ parameter["name"] ?? parameterName }}</code
+          >
           <div class="input-group">
-            <span class="input-group-text" :id="parameterName + '-help'">
-              <font-awesome-icon
-                class="me-2"
-                :icon="parameter['fa_icon']"
-                v-if="parameter['fa_icon']"
-              />
-              <code>--{{ parameterName }}</code>
+            <span class="input-group-text" v-if="parameter['fa_icon']">
+              <font-awesome-icon :icon="parameter['fa_icon']" />
             </span>
             <parameter-number-input
               v-if="
@@ -146,9 +143,11 @@ watch(
               class="input-group-text cursor-pointer px-2"
               v-if="parameter['help_text']"
               data-bs-toggle="collapse"
-              :data-bs-target="'#helpCollapse' + parameterName"
+              :data-bs-target="
+                '#helpCollapse' + parameterName.replace(/\./g, '')
+              "
               aria-expanded="false"
-              :aria-controls="'helpCollapse' + parameterName"
+              :aria-controls="'helpCollapse' + parameterName.replace(/\./g, '')"
             >
               <font-awesome-icon
                 class="cursor-pointer"
@@ -161,7 +160,7 @@ watch(
           </label>
           <div
             class="collapse"
-            :id="'helpCollapse' + parameterName"
+            :id="'helpCollapse' + parameterName.replace(/\./g, '')"
             v-if="parameter['help_text']"
           >
             <div class="p-2 pb-0 mx-2 mb-2 mt-1 flex-shrink-1 border rounded">
diff --git a/src/components/parameter-schema/form-mode/ParameterStringInput.vue b/src/components/parameter-schema/form-mode/ParameterStringInput.vue
index 0ba406b7f1eb838040cd2b60dc2101f1652bebb9..7d38aae038ea9d12f597f758a377fa08d7d42f81 100644
--- a/src/components/parameter-schema/form-mode/ParameterStringInput.vue
+++ b/src/components/parameter-schema/form-mode/ParameterStringInput.vue
@@ -1,10 +1,14 @@
 <script setup lang="ts">
-import { computed, watch, ref, onMounted, reactive } from "vue";
+import { computed, watch, onMounted, reactive } from "vue";
 import { useBucketStore } from "@/stores/buckets";
 import { useS3ObjectStore } from "@/stores/s3objects";
+import { useResourceStore } from "@/stores/resources";
+import { Status } from "@/client/resource";
 
 const bucketRepository = useBucketStore();
 const s3objectRepository = useS3ObjectStore();
+const resourceRepository = useResourceStore();
+const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 
 const props = defineProps({
   parameter: {
@@ -25,50 +29,49 @@ const props = defineProps({
   helpId: {
     type: String,
   },
+  removeAdvanced: {
+    type: Boolean,
+    default: false,
+  },
 });
 
-const randomIDSuffix = Math.random().toString(16).substring(2, 8);
-
-const defaultValue = computed<string>(() => props.parameter["default"]);
+const emit = defineEmits<{
+  (e: "update:modelValue", value?: string): void;
+}>();
 
 const s3Path = reactive<{
-  bucket: string | undefined;
-  key: string | undefined;
+  bucket: string;
+  key?: string;
 }>({
-  bucket: undefined,
+  bucket: "",
   key: undefined,
 });
 
-watch(defaultValue, (newVal, oldVal) => {
-  if (newVal != oldVal && newVal != undefined) {
-    emit("update:modelValue", newVal);
-  }
+const selectedResource = reactive<{
+  resourceId: string;
+  resourceVersionIndex: number;
+}>({
+  resourceId: "",
+  resourceVersionIndex: 0,
 });
 
-watch(s3Path, () => {
-  if (format.value) {
-    updateValue();
-  }
+const formState = reactive<{
+  advancedInput: boolean;
+  stringVal?: string;
+}>({
+  advancedInput: false,
+  stringVal: undefined,
 });
 
-watch(
-  () => s3Path.bucket,
-  (newVal, oldVal) => {
-    if (newVal !== oldVal) {
-      updateKeysInBucket(newVal);
-    }
-  },
-);
-
+const defaultValue = computed<string>(() => props.parameter["default"]);
 const pattern = computed<string>(() => props.parameter["pattern"]);
-
-const emit = defineEmits<{
-  (e: "update:modelValue", value: string | undefined): void;
-}>();
-
-const stringInput = ref<HTMLInputElement | undefined>(undefined);
-
-const format = computed<string | undefined>(() => props.parameter["format"]);
+const dataFormat = computed<string | undefined>(
+  () => props.parameter["format"],
+);
+const dataPath = computed<boolean>(() => dataFormat.value != undefined);
+const clowmResource = computed<boolean>(
+  () => props.parameter["clowm-resource"] ?? false,
+);
 
 const filesInBucket = computed<string[]>(() =>
   (s3objectRepository.objectMapping[s3Path.bucket ?? ""] ?? [])
@@ -98,7 +101,7 @@ const filesAndFoldersInBucket = computed<string[]>(() =>
 );
 
 const keyDataList = computed<string[]>(() => {
-  switch (format.value) {
+  switch (dataFormat.value) {
     case "file-path":
       return filesInBucket.value;
     case "directory-path":
@@ -110,23 +113,63 @@ const keyDataList = computed<string[]>(() => {
   }
 });
 
-function updateValue() {
-  if (format.value) {
-    emit(
-      "update:modelValue",
-      !s3Path.bucket && s3Path.key
-        ? undefined
-        : `s3://${s3Path.bucket}${s3Path.key ? "/" + s3Path.key : ""}`,
-    );
-  } else {
-    emit(
-      "update:modelValue",
-      stringInput.value?.value ? stringInput.value?.value : undefined,
-    );
+watch(defaultValue, (newVal, oldVal) => {
+  if (newVal != oldVal && newVal != undefined) {
+    emit("update:modelValue", newVal);
   }
+});
+
+watch(s3Path, () => {
+  if (dataPath.value && !formState.advancedInput) {
+    updateStringFromS3();
+  }
+});
+
+watch(
+  () => s3Path.bucket,
+  (newVal, oldVal) => {
+    if (newVal !== oldVal) {
+      updateKeysInBucket(newVal);
+    }
+  },
+);
+
+watch(() => formState.stringVal, updateValue);
+watch(selectedResource, () => {
+  if (clowmResource.value && !formState.advancedInput) {
+    updateStringFromResource();
+  }
+});
+
+watch(
+  () => formState.advancedInput,
+  (newVal, oldValue) => {
+    if (newVal != oldValue) {
+      if (clowmResource.value) {
+        updateStringFromResource();
+      } else if (dataPath.value) {
+        updateStringFromS3();
+      }
+    }
+  },
+);
+
+function updateValue() {
+  emit("update:modelValue", formState.stringVal);
 }
 
-const helpTextPresent = computed<boolean>(() => props.parameter["help_text"]);
+function updateStringFromS3() {
+  formState.stringVal = !s3Path.bucket
+    ? undefined
+    : `s3://${s3Path.bucket}${s3Path.key ? "/" + s3Path.key : ""}`;
+}
+
+function updateStringFromResource() {
+  formState.stringVal =
+    resourceRepository.resourceMapping[selectedResource.resourceId]?.versions[
+      selectedResource.resourceVersionIndex
+    ]?.cluster_path ?? undefined;
+}
 
 function updateKeysInBucket(bucketName?: string) {
   if (bucketName != null) {
@@ -138,14 +181,46 @@ function updateKeysInBucket(bucketName?: string) {
 }
 
 onMounted(() => {
-  if (format.value) {
-    s3Path.key = defaultValue.value;
-  }
+  formState.stringVal = defaultValue.value;
 });
 </script>
 
 <template>
-  <template v-if="format">
+  <template v-if="clowmResource && !formState.advancedInput">
+    <select
+      class="form-select"
+      :required="props.required"
+      v-model="selectedResource.resourceId"
+    >
+      <option selected disabled value="">Please select a resource</option>
+      <option
+        v-for="resource in resourceRepository.resources"
+        :key="resource.resource_id"
+        :value="resource.resource_id"
+      >
+        {{ resource.name }}
+      </option>
+    </select>
+    <select
+      class="form-select"
+      :required="props.required || selectedResource.resourceId != undefined"
+      v-model="selectedResource.resourceVersionIndex"
+      :disabled="selectedResource.resourceId.length === 0"
+    >
+      <option disabled>Please select a version</option>
+      <option
+        v-for="(version, index) in resourceRepository.resourceMapping[
+          selectedResource.resourceId
+        ]?.versions ?? []"
+        :key="version.resource_version_id"
+        :value="index"
+      >
+        {{ version.release }}
+        {{ version.status === Status.LATEST ? "- Latest" : "" }}
+      </option>
+    </select>
+  </template>
+  <template v-else-if="dataPath && !formState.advancedInput">
     <select
       class="form-select"
       :required="props.required"
@@ -162,10 +237,9 @@ onMounted(() => {
     </select>
     <input
       class="form-control"
-      :class="{ 'rounded-end': !helpTextPresent }"
       :list="'datalistOptions2' + randomIDSuffix"
       placeholder="Type to search in bucket..."
-      :required="props.required && format === 'file-path'"
+      :required="props.required && dataFormat === 'file-path'"
       v-model="s3Path.key"
       :pattern="pattern"
     />
@@ -173,17 +247,32 @@ onMounted(() => {
       <option v-for="obj in keyDataList" :value="obj" :key="obj" />
     </datalist>
   </template>
-  <input
-    v-else
-    ref="stringInput"
-    class="form-control"
-    type="text"
-    :value="props.modelValue"
-    :required="props.required"
-    :aria-describedby="props.helpId"
-    :pattern="pattern"
-    @input="updateValue"
-  />
+  <template v-else>
+    <input
+      ref="stringInput"
+      class="form-control"
+      type="text"
+      v-model="formState.stringVal"
+      :required="props.required"
+      :aria-describedby="props.helpId"
+      :pattern="pattern"
+    />
+  </template>
+  <div v-if="(clowmResource || dataPath) && !props.removeAdvanced">
+    <input
+      type="checkbox"
+      class="btn-check"
+      :id="'flexCheckDefault' + randomIDSuffix"
+      autocomplete="off"
+      v-model="formState.advancedInput"
+    />
+    <label
+      class="btn btn-outline-secondary rounded-end"
+      style="border-top-left-radius: 0; border-bottom-left-radius: 0"
+      :for="'flexCheckDefault' + randomIDSuffix"
+      >Advanced</label
+    >
+  </div>
 </template>
 
 <style scoped></style>
diff --git a/src/components/resources/ResourceCard.vue b/src/components/resources/ResourceCard.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ad96e63899a6a8b0f5f47a235e39d29658b4afa3
--- /dev/null
+++ b/src/components/resources/ResourceCard.vue
@@ -0,0 +1,337 @@
+<script setup lang="ts">
+import {
+  type ResourceOut,
+  type ResourceVersionOut,
+  Status,
+} from "@/client/resource";
+import { computed, onMounted, ref } from "vue";
+import dayjs from "dayjs";
+import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import { useS3ObjectStore } from "@/stores/s3objects";
+import { useResourceStore } from "@/stores/resources";
+import { Tooltip } from "bootstrap";
+import { useNameStore } from "@/stores/names";
+
+const randomIDSuffix: string = Math.random().toString(16).substring(2, 8);
+const objectRepository = useS3ObjectStore();
+const resourceRepository = useResourceStore();
+const nameRepository = useNameStore();
+
+const props = defineProps<{
+  resource: ResourceOut;
+  loading: boolean;
+  extended?: boolean;
+}>();
+let refreshTimeout: NodeJS.Timeout | undefined = undefined;
+
+const stateToUIMapping: Record<Status, string> = {
+  CLUSTER_DELETED: "Deleted on Cluster",
+  DENIED: "Rejected",
+  RESOURCE_REQUESTED: "Resource created",
+  S3_DELETED: "Deleted in S3",
+  SYNCHRONIZED: "Available",
+  SYNCHRONIZING: "Synchronizing to Cluster",
+  SYNC_REQUESTED: "Wait for Approval",
+  LATEST: "Available (Latest)",
+};
+
+const emit = defineEmits<{
+  (e: "click-info", resourceVersion: ResourceVersionOut): void;
+  (e: "click-update", resource: ResourceOut): void;
+}>();
+
+const resourceVersionS3Ready = ref<Record<string, boolean>>({});
+
+const resourceVersions = computed<ResourceVersionOut[]>(() =>
+  [...props.resource.versions].sort((a, b) =>
+    a.created_at < b.created_at ? 1 : -1,
+  ),
+);
+
+function checkS3Resource(resourceVersion: ResourceVersionOut) {
+  const bucket = resourceVersion.s3_path.slice(5).split("/")[0];
+  const key = resourceVersion.s3_path.split(bucket)[1].slice(1);
+  objectRepository
+    .fetchS3ObjectMeta(bucket, key)
+    .then(() => {
+      resourceVersionS3Ready.value[resourceVersion.resource_version_id] = true;
+    })
+    .catch(() => {
+      resourceVersionS3Ready.value[resourceVersion.resource_version_id] = false;
+    });
+}
+
+function clickCheckS3Resource(resourceVersion: ResourceVersionOut) {
+  clearTimeout(refreshTimeout);
+  refreshTimeout = setTimeout(() => {
+    checkS3Resource(resourceVersion);
+  }, 500);
+}
+
+function requestSynchronization(resourceVersion: ResourceVersionOut) {
+  resourceRepository.requestSynchronization(resourceVersion);
+}
+
+onMounted(() => {
+  if (!props.loading) {
+    for (const r of props.resource.versions) {
+      if (r.status == Status.RESOURCE_REQUESTED) {
+        checkS3Resource(r);
+      }
+    }
+    [
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
+      ...(document
+        .querySelector("#resource-card-" + randomIDSuffix)
+        ?.querySelectorAll("[data-bs-toggle='tooltip']") ?? []),
+    ].map((el) => new Tooltip(el));
+  }
+});
+</script>
+
+<template>
+  <div
+    :id="'resource-card-' + randomIDSuffix"
+    class="card-hover border border-secondary card m-2"
+  >
+    <div class="card-body">
+      <div
+        class="card-title fs-3 d-flex justify-content-between align-items-center"
+      >
+        <div v-if="props.loading" class="placeholder-glow w-100">
+          <span class="placeholder col-6"></span>
+        </div>
+        <div v-else>
+          <span>{{ props.resource.name }}</span>
+        </div>
+        <button
+          v-if="props.extended"
+          :disabled="props.loading"
+          class="btn btn-primary"
+          type="button"
+          data-bs-toggle="modal"
+          data-bs-target="#updateResourceModal"
+          @click="emit('click-update', props.resource)"
+        >
+          Update
+        </button>
+      </div>
+      <p class="card-text">
+        <span v-if="props.loading" class="placeholder-glow"
+          ><span class="placeholder col-12"></span
+        ></span>
+        <span v-else>{{ props.resource.description }}</span>
+        <br />
+        Source:
+        <span v-if="props.loading" class="placeholder-glow"
+          ><span class="placeholder col-2"></span
+        ></span>
+        <span v-else>{{ props.resource.source }}</span>
+      </p>
+      <div v-if="!props.loading">
+        <div class="accordion" :id="'accordion-' + props.resource.resource_id">
+          <div
+            v-for="resourceVersion of resourceVersions"
+            :key="resourceVersion.resource_version_id"
+            class="accordion-item"
+          >
+            <h2 class="accordion-header">
+              <button
+                class="accordion-button"
+                :class="{
+                  collapsed:
+                    resourceVersion.status != Status.LATEST || props.extended,
+                }"
+                type="button"
+                data-bs-toggle="collapse"
+                :data-bs-target="
+                  '#collapseResourceVersion-' +
+                  resourceVersion.resource_version_id
+                "
+                :aria-expanded="
+                  resourceVersion.status == Status.LATEST && !props.extended
+                "
+                :aria-controls="
+                  '#collapseResourceVersion-' +
+                  resourceVersion.resource_version_id
+                "
+              >
+                {{ resourceVersion.release }} -
+                {{ stateToUIMapping[resourceVersion.status] }}
+              </button>
+            </h2>
+            <div
+              :id="
+                'collapseResourceVersion-' + resourceVersion.resource_version_id
+              "
+              class="accordion-collapse collapse"
+              :class="{
+                show:
+                  resourceVersion.status == Status.LATEST && !props.extended,
+              }"
+              :data-bs-parent="'#accordion-' + props.resource.resource_id"
+            >
+              <div class="accordion-body">
+                <div>
+                  Registered at:
+                  {{
+                    dayjs.unix(resourceVersion.created_at).format("DD MMM YYYY")
+                  }}
+                </div>
+                <div
+                  v-if="
+                    props.extended &&
+                    (resourceVersion.status == Status.RESOURCE_REQUESTED ||
+                      resourceVersion.status == Status.CLUSTER_DELETED)
+                  "
+                >
+                  <div class="btn-group" role="group">
+                    <button
+                      type="button"
+                      class="btn btn-primary"
+                      data-bs-toggle="modal"
+                      data-bs-target="#uploadResourceInfoModal"
+                      @click="emit('click-info', resourceVersion)"
+                    >
+                      <font-awesome-icon icon="fa-solid fa-circle-question" />
+                    </button>
+                    <button
+                      type="button"
+                      class="btn btn-primary"
+                      :disabled="
+                        !resourceVersionS3Ready[
+                          resourceVersion.resource_version_id
+                        ]
+                      "
+                      @click="requestSynchronization(resourceVersion)"
+                    >
+                      Request Synchronization
+                    </button>
+                    <button
+                      v-if="resourceVersion.status == Status.RESOURCE_REQUESTED"
+                      type="button"
+                      class="btn btn-primary"
+                      @click="clickCheckS3Resource(resourceVersion)"
+                    >
+                      <font-awesome-icon
+                        icon="fa-solid fa-arrow-rotate-right"
+                      />
+                    </button>
+                  </div>
+                </div>
+                <div
+                  v-if="
+                    resourceVersion.status === Status.SYNCHRONIZED ||
+                    resourceVersion.status === Status.LATEST
+                  "
+                  class="my-1"
+                >
+                  <label
+                    :for="
+                      'nextflow-access-path-' +
+                      resourceVersion.resource_version_id
+                    "
+                    class="form-label"
+                    >Nextflow Access Path:</label
+                  >
+                  <div class="input-group fs-4 mb-3">
+                    <div
+                      class="input-group-text hover-info"
+                      :id="
+                        'tooltip-cluster-path-' +
+                        resourceVersion.resource_version_id
+                      "
+                      data-bs-toggle="tooltip"
+                      data-bs-title="Path on the cluster where a workflow can access the resource"
+                    >
+                      <font-awesome-icon icon="fa-solid fa-circle-question" />
+                    </div>
+                    <input
+                      :id="
+                        'nextflow-access-path-' +
+                        resourceVersion.resource_version_id
+                      "
+                      class="form-control"
+                      type="text"
+                      :value="resourceVersion.cluster_path"
+                      aria-label="Nextflow Access Path"
+                      readonly
+                    />
+                    <span class="input-group-text"
+                      ><copy-to-clipboard-icon
+                        :text="resourceVersion.cluster_path ?? ''"
+                    /></span>
+                  </div>
+                </div>
+                <div
+                  v-if="
+                    props.extended &&
+                    resourceVersion.status !== Status.S3_DELETED
+                  "
+                  class="my-1"
+                >
+                  <label
+                    :for="
+                      's3-access-path-' + resourceVersion.resource_version_id
+                    "
+                    class="form-label"
+                    >S3 Upload Path:</label
+                  >
+                  <div class="input-group fs-4 mb-3">
+                    <div
+                      class="input-group-text hover-info"
+                      :id="
+                        'tooltip-s3-path-' + resourceVersion.resource_version_id
+                      "
+                      data-bs-toggle="tooltip"
+                      data-bs-title="S3 Path where the resource should be uploaded"
+                    >
+                      <font-awesome-icon icon="fa-solid fa-circle-question" />
+                    </div>
+                    <input
+                      :id="
+                        's3-access-path-' + resourceVersion.resource_version_id
+                      "
+                      class="form-control"
+                      type="text"
+                      :value="resourceVersion.s3_path"
+                      aria-label="S3 Access Path"
+                      readonly
+                    />
+                    <span class="input-group-text"
+                      ><copy-to-clipboard-icon :text="resourceVersion.s3_path"
+                    /></span>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="mt-2">
+        Maintainer:
+        <span
+          v-if="
+            props.loading || !nameRepository.getName(resource.maintainer_id)
+          "
+          class="placeholder-glow"
+          ><span class="placeholder col-2"></span
+        ></span>
+        <span v-else>{{ nameRepository.getName(resource.maintainer_id) }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.card-hover {
+  transition: transform 0.3s ease-out;
+}
+
+.card-hover:hover {
+  transform: translate(0, -5px);
+  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
+}
+</style>
diff --git a/src/components/resources/ResourceVersionInfoModal.vue b/src/components/resources/ResourceVersionInfoModal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1fb0e2fdff5f56d9899938a9d9febcdb9b49c1ef
--- /dev/null
+++ b/src/components/resources/ResourceVersionInfoModal.vue
@@ -0,0 +1,264 @@
+<script setup lang="ts">
+import BootstrapModal from "@/components/modals/BootstrapModal.vue";
+import type { ResourceVersionOut, ResourceOut } from "@/client/resource";
+import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue";
+import dayjs from "dayjs";
+import { computed } from "vue";
+import { useNameStore } from "@/stores/names";
+
+const props = defineProps<{
+  modalId: string;
+  resourceVersionIndex: number;
+  resource?: ResourceOut;
+}>();
+
+const nameRepository = useNameStore();
+
+const resourceVersion = computed<ResourceVersionOut | undefined>(
+  () => props.resource?.versions[props.resourceVersionIndex],
+);
+</script>
+
+<template>
+  <bootstrap-modal
+    :modal-id="props.modalId"
+    modal-label="Resource Version Info Modal"
+    size-modifier="lg"
+  >
+    <template #header
+      >Resource
+      <b v-if="resourceVersion"
+        >{{ resource?.name }}@{{ resourceVersion?.release }}</b
+      >
+    </template>
+    <template #body>
+      <h5>Resource</h5>
+      <div class="mb-3 row">
+        <div class="col-8">
+          <label
+            for="resource-version-info-modal-resource-id"
+            class="form-label"
+            >ID</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resource-version-info-modal-resource-id"
+              readonly
+              :value="props.resource?.resource_id"
+            />
+            <span class="input-group-text"
+              ><copy-to-clipboard-icon
+                :text="props.resource?.resource_id ?? ''"
+            /></span>
+          </div>
+        </div>
+        <div class="col-4">
+          <label
+            for="resource-version-info-modal-resource-name"
+            class="form-label"
+            >Name</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resource-version-info-modal-resource-name"
+              readonly
+              :value="props.resource?.name"
+            />
+          </div>
+        </div>
+      </div>
+      <div class="mb-3 row">
+        <div class="col-8">
+          <label
+            for="resource-version-info-modal-maintainer-id"
+            class="form-label"
+            >Maintainer ID</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resource-version-info-modal-maintainer-id"
+              readonly
+              :value="props.resource?.maintainer_id"
+            />
+            <span class="input-group-text"
+              ><copy-to-clipboard-icon
+                :text="props.resource?.maintainer_id ?? ''"
+            /></span>
+          </div>
+        </div>
+        <div class="col-4">
+          <label
+            for="resource-version-info-modal-maintainer-name"
+            class="form-label"
+            >Maintainer Name</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resource-version-info-modal-maintainer-name"
+              readonly
+              :value="nameRepository.getName(props.resource?.maintainer_id)"
+            />
+          </div>
+        </div>
+      </div>
+      <div class="mb-3">
+        <label
+          for="resource-version-info-modal-resource-description"
+          class="form-label"
+          >Description</label
+        >
+        <div class="input-group">
+          <textarea
+            class="form-control"
+            id="resource-version-info-modal-resource-description"
+            readonly
+            rows="2"
+            :value="props.resource?.description"
+          />
+        </div>
+      </div>
+      <div class="mb-5">
+        <label
+          for="resource-version-info-modal-resource-source"
+          class="form-label"
+          >Source</label
+        >
+        <div class="input-group">
+          <input
+            type="text"
+            class="form-control"
+            id="resource-version-info-modal-resource-source"
+            readonly
+            :value="props.resource?.source"
+          />
+        </div>
+      </div>
+      <h5>Resource Version</h5>
+      <div class="mb-3 row">
+        <div class="col-8">
+          <label
+            for="resource-version-info-modal-resource-version-id"
+            class="form-label"
+            >Resource Version ID</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resource-version-info-modal-resource-version-id"
+              readonly
+              :value="resourceVersion?.resource_version_id"
+            />
+            <span class="input-group-text"
+              ><copy-to-clipboard-icon
+                :text="resourceVersion?.resource_version_id ?? ''"
+            /></span>
+          </div>
+        </div>
+        <div class="col-4">
+          <label
+            for="resource-version-info-modal-resource-version-release"
+            class="form-label"
+            >Resource Version Release</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resource-version-info-modal-resource-version-release"
+              readonly
+              :value="resourceVersion?.release"
+            />
+          </div>
+        </div>
+      </div>
+      <div class="mb-3 row">
+        <div class="col-4">
+          <label
+            for="resource-version-info-modal-resource-version-status"
+            class="form-label"
+            >Status</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resource-version-info-modal-resource-version-status"
+              readonly
+              :value="resourceVersion?.status"
+            />
+          </div>
+        </div>
+        <div class="col-4">
+          <label
+            for="resource-version-info-modal-resource-version-timestamp"
+            class="form-label"
+            >Created At</label
+          >
+          <div class="input-group">
+            <input
+              type="datetime-local"
+              class="form-control"
+              id="resource-version-info-modal-resource-version-timestamp"
+              readonly
+              :value="
+                dayjs
+                  .unix(resourceVersion?.created_at ?? 0)
+                  .format('YYYY-MM-DDTHH:mm')
+              "
+            />
+          </div>
+        </div>
+      </div>
+      <div class="mb-3">
+        <label
+          for="resource-version-info-modal-resource-version-s3-path"
+          class="form-label"
+          >S3 Path</label
+        >
+        <div class="input-group">
+          <input
+            type="text"
+            class="form-control"
+            id="resource-version-info-modal-resource-version-s3-path"
+            readonly
+            :value="resourceVersion?.s3_path"
+          />
+          <span class="input-group-text"
+            ><copy-to-clipboard-icon :text="resourceVersion?.s3_path ?? ''"
+          /></span>
+        </div>
+      </div>
+      <div class="mb-3">
+        <label
+          for="resource-version-info-modal-resource-version-cluster-path"
+          class="form-label"
+          >Cluster Path</label
+        >
+        <div class="input-group">
+          <input
+            type="text"
+            class="form-control"
+            id="resource-version-info-modal-resource-version-cluster-path"
+            readonly
+            :value="resourceVersion?.cluster_path"
+          />
+          <span class="input-group-text"
+            ><copy-to-clipboard-icon
+              :text="resourceVersion?.cluster_path ?? ''"
+          /></span>
+        </div>
+      </div>
+    </template>
+  </bootstrap-modal>
+</template>
+
+<style scoped></style>
diff --git a/src/components/resources/modals/CreateResourceModal.vue b/src/components/resources/modals/CreateResourceModal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8bf7a012971ac41ea5a94865e3018333f417986c
--- /dev/null
+++ b/src/components/resources/modals/CreateResourceModal.vue
@@ -0,0 +1,229 @@
+<script setup lang="ts">
+import { reactive, onMounted, ref } from "vue";
+import BootstrapModal from "@/components/modals/BootstrapModal.vue";
+import { Modal } from "bootstrap";
+import { useResourceStore } from "@/stores/resources";
+import type { ResourceIn } from "@/client/resource";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import { Tooltip } from "bootstrap";
+
+const resourceRepository = useResourceStore();
+
+const resourceCreateForm = ref<HTMLFormElement | undefined>(undefined);
+const resourceNameElement = ref<HTMLInputElement | undefined>(undefined);
+
+const resource = reactive<ResourceIn>({
+  name: "",
+  description: "",
+  release: "",
+  source: "",
+});
+
+const formState = reactive<{
+  validated: boolean;
+  resourceNameTaken: boolean;
+  loading: boolean;
+}>({
+  validated: false,
+  resourceNameTaken: false,
+  loading: false,
+});
+
+const props = defineProps<{
+  modalID: string;
+}>();
+
+let createResourceModal: Modal | null = null;
+
+function createResource() {
+  formState.validated = true;
+  formState.resourceNameTaken = false;
+  resource.description = resource.description.trim();
+  resource.name = resource.name.trim();
+  resourceNameElement.value?.setCustomValidity("");
+  if (resourceCreateForm.value?.checkValidity()) {
+    formState.loading = true;
+    resourceRepository
+      .createResource(resource)
+      .then(() => {
+        createResourceModal?.hide();
+        resource.name = "";
+        resource.description = "";
+        resource.source = "";
+        resource.release = "";
+        formState.resourceNameTaken = false;
+        formState.validated = false;
+      })
+      .catch((error) => {
+        if (
+          error.status === 400 &&
+          error.body["detail"] ===
+            `Resource with name '${resource.name}' already exists`
+        ) {
+          formState.resourceNameTaken = true;
+          resourceNameElement.value?.setCustomValidity(
+            "Resource name is already taken",
+          );
+        }
+      })
+      .finally(() => {
+        formState.loading = false;
+      });
+  }
+}
+
+function modalClosed() {
+  formState.validated = false;
+  formState.resourceNameTaken = false;
+  resourceNameElement.value?.setCustomValidity("");
+}
+
+onMounted(() => {
+  createResourceModal = new Modal("#" + props.modalID);
+  new Tooltip("#tooltip-new-resource-source");
+  new Tooltip("#tooltip-new-resource-release");
+});
+</script>
+
+<template>
+  <bootstrap-modal
+    :modalId="modalID"
+    static-backdrop
+    modal-label="Create Resource Modal"
+    v-on="{ 'hidden.bs.modal': modalClosed }"
+  >
+    <template #header> Create new Resource</template>
+    <template #body>
+      <form
+        id="resourceCreateForm"
+        :class="{ 'was-validated': formState.validated }"
+        ref="resourceCreateForm"
+      >
+        <div class="mb-3">
+          <label for="resourceNameInput" class="form-label"
+            >Resource Name</label
+          >
+          <div class="input-group">
+            <input
+              type="text"
+              class="form-control"
+              id="resourceNameInput"
+              required
+              minlength="3"
+              maxlength="32"
+              v-model="resource.name"
+              ref="resourceNameElement"
+            />
+            <div class="invalid-feedback">
+              <div v-if="formState.resourceNameTaken">
+                Resource name already taken.
+              </div>
+              <div>
+                Requirements
+                <ul>
+                  <li>At least 3 Characters long</li>
+                  <li>Unique in CloWM</li>
+                </ul>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="mb-3">
+          <label for="resourceDescriptionInput" class="form-label">
+            Description
+          </label>
+          <div class="input-group">
+            <textarea
+              class="form-control"
+              id="resourceDescriptionInput"
+              required
+              rows="3"
+              minlength="16"
+              maxlength="256"
+              v-model="resource.description"
+              placeholder="Describe the purpose of the resource"
+            ></textarea>
+            <div class="invalid-feedback">
+              Requirements
+              <ul>
+                <li>At least 16 Characters long</li>
+              </ul>
+            </div>
+          </div>
+        </div>
+        <div class="mb-3">
+          <label for="resourceSourceInput" class="form-label"> Source </label>
+          <div class="input-group">
+            <div class="input-group-text">
+              <font-awesome-icon icon="fa-solid fa-link" />
+            </div>
+            <input
+              class="form-control"
+              id="resourceSourceInput"
+              required
+              minlength="8"
+              maxlength="264"
+              v-model="resource.source"
+              placeholder="The source of the resource (e.g. a link)"
+            />
+            <div
+              class="input-group-text hover-info"
+              id="tooltip-new-resource-source"
+              data-bs-toggle="tooltip"
+              data-bs-title="The source from where the resource comes"
+            >
+              <font-awesome-icon icon="fa-solid fa-circle-question" />
+            </div>
+          </div>
+        </div>
+        <div class="mb-3">
+          <label for="resourceReleaseInput" class="form-label"> Release </label>
+          <div class="input-group">
+            <div class="input-group-text">
+              <font-awesome-icon icon="fa-solid fa-tag" />
+            </div>
+            <input
+              class="form-control"
+              id="resourceReleaseInput"
+              required
+              minlength="3"
+              maxlength="32"
+              v-model="resource.release"
+              placeholder="Initial release name"
+            />
+            <div
+              class="input-group-text hover-info"
+              id="tooltip-new-resource-release"
+              data-bs-toggle="tooltip"
+              data-bs-title="The name of the first resource version"
+            >
+              <font-awesome-icon icon="fa-solid fa-circle-question" />
+            </div>
+          </div>
+        </div>
+      </form>
+    </template>
+    <template v-slot:footer>
+      <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
+        Close
+      </button>
+      <button
+        type="submit"
+        form="resourceCreateForm"
+        class="btn btn-primary"
+        :disabled="formState.loading"
+        @click.prevent="createResource"
+      >
+        <span
+          v-if="formState.loading"
+          class="spinner-border spinner-border-sm"
+          role="status"
+          aria-hidden="true"
+        ></span>
+        Save
+      </button>
+    </template>
+  </bootstrap-modal>
+</template>
+
+<style scoped></style>
diff --git a/src/components/resources/modals/UpdateResourceModal.vue b/src/components/resources/modals/UpdateResourceModal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..f0e73c341af5384ae0b64216b26eb02a86e782c9
--- /dev/null
+++ b/src/components/resources/modals/UpdateResourceModal.vue
@@ -0,0 +1,129 @@
+<script setup lang="ts">
+import { reactive, onMounted, ref } from "vue";
+import BootstrapModal from "@/components/modals/BootstrapModal.vue";
+import { Modal } from "bootstrap";
+import { useResourceStore } from "@/stores/resources";
+import type { ResourceVersionIn, ResourceOut } from "@/client/resource";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import { Tooltip } from "bootstrap";
+
+const resourceRepository = useResourceStore();
+
+const resourceUpdateForm = ref<HTMLFormElement | undefined>(undefined);
+
+const resourceUpdate = reactive<ResourceVersionIn>({
+  release: "",
+});
+
+const formState = reactive<{
+  validated: boolean;
+  loading: boolean;
+}>({
+  validated: false,
+  loading: false,
+});
+
+const props = defineProps<{
+  modalId: string;
+  resource: ResourceOut;
+}>();
+
+let updateResourceModal: Modal | null = null;
+
+function updateResource() {
+  formState.validated = true;
+  resourceUpdate.release = resourceUpdate.release.trim();
+  if (resourceUpdateForm.value?.checkValidity()) {
+    formState.loading = true;
+    resourceRepository
+      .updateResource(props.resource.resource_id, resourceUpdate)
+      .then(() => {
+        updateResourceModal?.hide();
+        resourceUpdate.release = "";
+        formState.validated = false;
+      })
+      .finally(() => {
+        formState.loading = false;
+      });
+  }
+}
+
+function modalClosed() {
+  formState.validated = false;
+}
+
+onMounted(() => {
+  updateResourceModal = new Modal("#" + props.modalId);
+  new Tooltip("#tooltip-update-resource-release");
+});
+</script>
+
+<template>
+  <bootstrap-modal
+    :modalId="modalId"
+    static-backdrop
+    modal-label="Update Resource Modal"
+    v-on="{ 'hidden.bs.modal': modalClosed }"
+  >
+    <template #header>
+      Update Resource <b>{{ props.resource.name }}</b></template
+    >
+    <template #body>
+      <form
+        id="resourceUpdateForm"
+        :class="{ 'was-validated': formState.validated }"
+        ref="resourceUpdateForm"
+      >
+        <div class="mb-3">
+          <label for="resourceUpdateReleaseInput" class="form-label">
+            Release
+          </label>
+          <div class="input-group">
+            <div class="input-group-text">
+              <font-awesome-icon icon="fa-solid fa-tag" />
+            </div>
+            <input
+              class="form-control"
+              id="resourceUpdateReleaseInput"
+              required
+              minlength="3"
+              maxlength="32"
+              v-model="resourceUpdate.release"
+              placeholder="Next release name"
+            />
+            <div
+              class="input-group-text hover-info"
+              id="tooltip-update-resource-release"
+              data-bs-toggle="tooltip"
+              data-bs-title="The name of the next resource version"
+            >
+              <font-awesome-icon icon="fa-solid fa-circle-question" />
+            </div>
+          </div>
+        </div>
+      </form>
+    </template>
+    <template v-slot:footer>
+      <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
+        Close
+      </button>
+      <button
+        type="submit"
+        form="resourceUpdateForm"
+        class="btn btn-primary"
+        :disabled="formState.loading || !props.resource.resource_id"
+        @click.prevent="updateResource"
+      >
+        <span
+          v-if="formState.loading"
+          class="spinner-border spinner-border-sm"
+          role="status"
+          aria-hidden="true"
+        ></span>
+        Save
+      </button>
+    </template>
+  </bootstrap-modal>
+</template>
+
+<style scoped></style>
diff --git a/src/components/resources/modals/UploadResourceInfoModal.vue b/src/components/resources/modals/UploadResourceInfoModal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..10463ea778ce2f0f11991e61470337f786da86aa
--- /dev/null
+++ b/src/components/resources/modals/UploadResourceInfoModal.vue
@@ -0,0 +1,337 @@
+<script setup lang="ts">
+import BootstrapModal from "@/components/modals/BootstrapModal.vue";
+import { type ResourceVersionOut, Status } from "@/client/resource";
+import { computed, onMounted, ref, watch } from "vue";
+import { environment } from "@/environment";
+import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue";
+import { useS3KeyStore } from "@/stores/s3keys";
+import type { S3Key } from "@/client/s3proxy";
+import { useS3ObjectStore } from "@/stores/s3objects";
+import { useResourceStore } from "@/stores/resources";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import { Modal } from "bootstrap";
+
+const props = defineProps<{
+  modalId: string;
+  resourceVersion?: ResourceVersionOut;
+}>();
+
+const s3KeyRepository = useS3KeyStore();
+const objectRepository = useS3ObjectStore();
+const resourceRepository = useResourceStore();
+
+let infoResourceModal: Modal | null = null;
+let refreshTimeout: NodeJS.Timeout | undefined = undefined;
+
+enum Tool {
+  PYTHON = "python",
+  S5CMD = "s5cmd",
+  MINIO = "minio",
+}
+
+watch(
+  () => props.resourceVersion,
+  (newVersion, oldVersion) => {
+    if (newVersion?.resource_version_id !== oldVersion?.resource_version_id) {
+      resourceSynchronizationEnabled.value = false;
+      if (newVersion?.status === Status.RESOURCE_REQUESTED) {
+        checkS3Resource(newVersion);
+      }
+    }
+  },
+);
+
+const activeTool = ref<Tool>(Tool.S5CMD);
+const resourceSynchronizationEnabled = ref<boolean>(false);
+
+const resourceS3Path = computed<string>(() => {
+  return (
+    props.resourceVersion?.s3_path ??
+    "s3://examplebucket/RESOURCE-ID/RESOURCE-VERSION-ID/resource.tar.gz"
+  );
+});
+const resourceMinioS3Path = computed<string>(() => {
+  return resourceS3Path.value.slice(5);
+});
+const resourceBucket = computed<string>(() => {
+  return resourceMinioS3Path.value.split("/")[0];
+});
+const resourceKey = computed<string>(() => {
+  return resourceS3Path.value.split(resourceBucket.value)[1].slice(1);
+});
+const codeExample = computed<string>(() => {
+  if (activeTool.value === Tool.S5CMD) {
+    return `export AWS_REGION="us-west-1"
+export AWS_ACCESS_KEY_ID="${s3Key.value.access_key}"
+export AWS_SECRET_ACCESS_KEY="${s3Key.value.secret_key}"
+export S3_ENDPOINT="${environment.S3_URL}"
+
+s5cmd cp --show-progress /PATH/TO/RESOURCE \\
+  ${resourceS3Path.value}`;
+  } else if (activeTool.value === Tool.MINIO) {
+    return `mc alias set ${environment.S3_URL} "${s3Key.value.access_key}" "${s3Key.value.secret_key}"
+mc cp /PATH/TO/RESOURCE \\
+  clowm-s3/clowm-resources/CLDB-eaa7aae4/eaaa1c99b14911ee9f5d0242ac120004/resource.tar.gz`;
+  } else if (activeTool.value === Tool.PYTHON) {
+    return `import boto3
+
+s3 = boto3.resource(
+  service_name="s3",
+  aws_access_key_id="${s3Key.value.access_key}",
+  aws_secret_access_key="${s3Key.value.secret_key}",
+  endpoint_url="${environment.S3_URL}",
+  verify=True,
+)
+
+with open("/PATH/TO/RESOURCE", "rb") as f:
+  s3.Object(
+    bucket="${resourceBucket.value}",
+    key="${resourceKey.value}"
+  ).upload_fileobj(f)`;
+  }
+  return "";
+});
+
+const s3Key = computed<S3Key>(() => {
+  return (
+    s3KeyRepository.keys[0] ?? {
+      access_key: "abc",
+      secret_key: "def",
+    }
+  );
+});
+
+function checkS3Resource(resourceVersion: ResourceVersionOut) {
+  const bucket = resourceVersion.s3_path.slice(5).split("/")[0];
+  const key = resourceVersion.s3_path.split(bucket)[1].slice(1);
+  objectRepository
+    .fetchS3ObjectMeta(bucket, key)
+    .then(() => {
+      resourceSynchronizationEnabled.value = true;
+    })
+    .catch(() => {
+      resourceSynchronizationEnabled.value = false;
+    });
+}
+
+function clickCheckS3Resource(resourceVersion: ResourceVersionOut) {
+  clearTimeout(refreshTimeout);
+  refreshTimeout = setTimeout(() => {
+    checkS3Resource(resourceVersion);
+  }, 500);
+}
+
+function requestSynchronization(resourceVersion: ResourceVersionOut) {
+  resourceRepository.requestSynchronization(resourceVersion).then(() => {
+    infoResourceModal?.hide();
+  });
+}
+
+onMounted(() => {
+  infoResourceModal = new Modal("#" + props.modalId);
+});
+</script>
+
+<template>
+  <bootstrap-modal
+    :modalId="props.modalId"
+    modal-label="Upload Resource Info Modal"
+    sizeModifier="lg"
+  >
+    <template #header>How to upload a resource to the cluster</template>
+    <template #body>
+      <ol>
+        <li :class="{ 'text-decoration-line-through': props.resourceVersion }">
+          <h6>
+            Prerequisite: Prepare a single compressed tar archive of your data
+          </h6>
+          <p :hidden="props.resourceVersion != undefined">
+            The data of your resource must be compressed into a single tar
+            archive to save bandwidth and storage space.<br />
+            <code>tar -czf resource.tar.gz /PATH/TO/MY/RESOURCE/DATA</code>
+          </p>
+        </li>
+        <li :class="{ 'text-decoration-line-through': props.resourceVersion }">
+          <h6>Request a resource in CloWM</h6>
+          <p :hidden="props.resourceVersion != undefined">
+            Click on <b>Create Resource</b> and fill out the form
+          </p>
+        </li>
+        <li
+          :class="{
+            'text-decoration-line-through': resourceSynchronizationEnabled,
+          }"
+        >
+          <h6>Upload the resource</h6>
+          <template v-if="!resourceSynchronizationEnabled">
+            <p>
+              Upload the tar archive to the provided S3 path with a tool of your
+              choice.
+            </p>
+            <ul class="nav nav-tabs mb-2">
+              <li class="nav-item">
+                <a
+                  class="nav-link"
+                  :class="{ active: activeTool === Tool.S5CMD }"
+                  aria-current="page"
+                  href="#"
+                  @click="activeTool = Tool.S5CMD"
+                  >s5cmd</a
+                >
+              </li>
+              <li class="nav-item">
+                <a
+                  class="nav-link"
+                  :class="{ active: activeTool === Tool.MINIO }"
+                  href="#"
+                  @click="activeTool = Tool.MINIO"
+                  >Minio</a
+                >
+              </li>
+              <li class="nav-item">
+                <a
+                  class="nav-link"
+                  :class="{ active: activeTool === Tool.PYTHON }"
+                  href="#"
+                  @click="activeTool = Tool.PYTHON"
+                  >Python</a
+                >
+              </li>
+            </ul>
+            <template v-if="activeTool === Tool.S5CMD">
+              <p>
+                <code>s5cmd</code> is a very fast S3 and local filesystem
+                execution tool. It comes with support for a multitude of
+                operations including tab completion and wildcard support for
+                files, which can be very handy for your object storage workflow
+                while working with large number of files.
+              </p>
+              <p>
+                The GitHub repository contains a
+                <a
+                  href="https://github.com/peak/s5cmd?tab=readme-ov-file#installation"
+                  target="_blank"
+                  >installation guide</a
+                >.
+              </p>
+              <pre class="w-100"><code class="hljs language-bash"><span
+                  class="hljs-built_in">export</span> AWS_REGION=<span class="hljs-string">"us-west-1"</span>
+<span class="hljs-built_in">export</span> AWS_ACCESS_KEY_ID=<span class="hljs-string">"{{ s3Key.access_key }}"</span>
+<span class="hljs-built_in">export</span> AWS_SECRET_ACCESS_KEY=<span class="hljs-string">"{{
+                    s3Key.secret_key
+                  }}"</span>
+<span class="hljs-built_in">export</span> S3_ENDPOINT=<span class="hljs-string">"{{ environment.S3_URL }}"</span>
+
+<span class="hljs-built_in">s5cmd</span> cp --show-progress /PATH/TO/RESOURCE \
+  {{ resourceS3Path }}</code></pre>
+            </template>
+            <template v-else-if="activeTool === Tool.MINIO">
+              <p>
+                The MinIO Client <code>mc</code> command line tool provides a
+                modern alternative to UNIX commands like <code>ls</code>,
+                <code>cat</code>, <code>cp</code>, <code>mirror</code>, and
+                <code>diff</code> with support for both filesystems and Amazon
+                S3-compatible cloud storage services.
+              </p>
+              <p>
+                The official documentation contains a
+                <a
+                  href="https://min.io/docs/minio/linux/reference/minio-mc.html#install-mc"
+                  target="_blank"
+                  >installation guide</a
+                >.
+              </p>
+              <pre
+                class="w-100"
+              ><code class="hljs language-bash">mc <span class="hljs-built_in">alias</span> <span
+                  class="hljs-built_in">set</span> {{ environment.S3_URL }} <span
+                  class="hljs-string">"{{ s3Key.access_key }}"</span> <span
+                  class="hljs-string">"{{ s3Key.secret_key }}"</span>
+mc <span class="hljs-built_in">cp</span> /PATH/TO/RESOURCE \
+  clowm-s3/{{ resourceMinioS3Path }}</code></pre>
+            </template>
+            <template v-else-if="activeTool === Tool.PYTHON">
+              <p>
+                You use the AWS SDK for Python (<a
+                  href="https://boto3.amazonaws.com/v1/documentation/api/latest/index.html"
+                  target="_blank"
+                  >Boto3</a
+                >) to create, configure, and manage AWS services, such as Amazon
+                Elastic Compute Cloud (Amazon EC2) and Amazon Simple Storage
+                Service (Amazon S3). The SDK provides an object-oriented API as
+                well as low-level access to AWS services.
+              </p>
+              <p>
+                The package can be installed with pip:
+                <code>pip install boto3</code>
+              </p>
+              <pre
+                class="w-100"
+              ><code class="hljs language-python"><span class="hljs-keyword">import</span> boto3
+
+s3 = boto3.resource(
+  service_name=<span class="hljs-string">"s3"</span>,
+  aws_access_key_id=<span class="hljs-string">"{{ s3Key.access_key }}"</span>,
+  aws_secret_access_key=<span class="hljs-string">"{{ s3Key.secret_key }}"</span>,
+  endpoint_url=<span class="hljs-string">"{{ environment.S3_URL }}"</span>,
+  verify=<span class="hljs-literal">True</span>,
+)
+
+<span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(<span class="hljs-string">"/PATH/TO/RESOURCE"</span>, <span
+                    class="hljs-string">"rb"</span>) <span class="hljs-keyword">as</span> f:
+  s3.Object(
+    bucket=<span class="hljs-string">"{{ resourceBucket }}"</span>,
+    key=<span class="hljs-string">"{{ resourceKey }}"</span>
+  ).upload_fileobj(f)</code></pre>
+            </template>
+          </template>
+        </li>
+        <li>
+          <h6>Request a synchronization</h6>
+          <p>
+            Click <b>Request Synchronization</b> to request the synchronization
+            to CloWM's compute cluster.
+          </p>
+          <div class="btn-group" role="group" v-if="props.resourceVersion">
+            <button
+              type="button"
+              class="btn btn-primary"
+              :disabled="!resourceSynchronizationEnabled"
+              @click="requestSynchronization(props.resourceVersion)"
+            >
+              Request Synchronization
+            </button>
+            <button
+              v-if="props.resourceVersion.status === Status.RESOURCE_REQUESTED"
+              type="button"
+              class="btn btn-primary"
+              @click="clickCheckS3Resource(props.resourceVersion)"
+            >
+              <font-awesome-icon icon="fa-solid fa-arrow-rotate-right" />
+            </button>
+          </div>
+        </li>
+        <li>
+          <h6>Resource availability</h6>
+          <p>
+            Once a Reviewer approves your resource synchronization request, the
+            resource will be made available in CloWM and is accessible for every
+            workflow via its <b>Nextflow Access Path</b>.
+          </p>
+        </li>
+      </ol>
+    </template>
+    <template #footer>
+      <copy-to-clipboard-icon
+        v-if="props.resourceVersion && !resourceSynchronizationEnabled"
+        button
+        :text="codeExample"
+      />
+      <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
+        Close
+      </button>
+    </template>
+  </bootstrap-modal>
+</template>
+
+<style scoped></style>
diff --git a/src/components/workflows/WorkflowStatisticsChart.vue b/src/components/workflows/WorkflowStatisticsChart.vue
index ee4711cfc8829daa8390973a303b1af6aaf1da07..56a9648686e8d2b153373cc096bc43fdf0366cc5 100644
--- a/src/components/workflows/WorkflowStatisticsChart.vue
+++ b/src/components/workflows/WorkflowStatisticsChart.vue
@@ -135,7 +135,7 @@ onMounted(() => {
     chart = new Chart(canvas.value, {
       type: "bar",
       data: {
-        labels: [], // Days as lables
+        labels: [], // Days as labels
         datasets: [
           {
             // Workflow count per day
diff --git a/src/components/workflows/modals/ArbitraryWorkflowModal.vue b/src/components/workflows/modals/ArbitraryWorkflowModal.vue
index 65fce9d4fdd315b462ea2d30d1efef49c07a3238..c33d00d11f856eeb47d9f0dba00045f31215723a 100644
--- a/src/components/workflows/modals/ArbitraryWorkflowModal.vue
+++ b/src/components/workflows/modals/ArbitraryWorkflowModal.vue
@@ -186,7 +186,7 @@ onMounted(() => {
 
 <template>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="false"
     modal-label="Create Workflow Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
@@ -424,8 +424,4 @@ onMounted(() => {
   </bootstrap-modal>
 </template>
 
-<style scoped>
-.hover-info:hover {
-  color: var(--bs-info) !important;
-}
-</style>
+<style scoped></style>
diff --git a/src/components/workflows/modals/CreateWorkflowModal.vue b/src/components/workflows/modals/CreateWorkflowModal.vue
index 86bd5862fcfe811428259c97758a9fb9ca299ee7..3d7d1bcca8a6ed80cb6532d6c2b3628d52897e04 100644
--- a/src/components/workflows/modals/CreateWorkflowModal.vue
+++ b/src/components/workflows/modals/CreateWorkflowModal.vue
@@ -17,6 +17,7 @@ import {
 import { valid } from "semver";
 import WorkflowModeTransitionGroup from "@/components/transitions/WorkflowModeTransitionGroup.vue";
 import { useWorkflowStore } from "@/stores/workflows";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const workflowRepository = useWorkflowStore();
 // Emitted Events
@@ -319,28 +320,11 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">Successfully created Workflow</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix">
+    Successfully created Workflow
+  </bootstrap-toast>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Create Workflow Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
@@ -700,8 +684,4 @@ onMounted(() => {
   </bootstrap-modal>
 </template>
 
-<style scoped>
-.hover-info:hover {
-  color: var(--bs-info) !important;
-}
-</style>
+<style scoped></style>
diff --git a/src/components/workflows/modals/ParameterModal.vue b/src/components/workflows/modals/ParameterModal.vue
index 576746b44668556bf079f70a015d88b9bd1315c1..76f0d35f2ae516fd616ec0baf486931dc183fd79 100644
--- a/src/components/workflows/modals/ParameterModal.vue
+++ b/src/components/workflows/modals/ParameterModal.vue
@@ -6,10 +6,10 @@ import type { RouteParamsRaw } from "vue-router";
 import { Modal } from "bootstrap";
 import { useRouter } from "vue-router";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
-import { useWorkflowStore } from "@/stores/workflows";
 import type { WorkflowExecutionOut } from "@/client/workflow";
+import { useNameStore } from "@/stores/names";
 
-const workflowRepository = useWorkflowStore();
+const nameRepository = useNameStore();
 const executionRepository = useWorkflowExecutionStore();
 const router = useRouter();
 
@@ -71,9 +71,9 @@ const workflowName = computed<string>(() => {
       execution?.workflow_version_id != undefined
     ) {
       return (
-        workflowRepository.getName(execution.workflow_id) +
+        nameRepository.getName(execution.workflow_id) +
         "@" +
-        workflowRepository.getName(execution.workflow_version_id)
+        nameRepository.getName(execution.workflow_version_id)
       );
     }
   }
@@ -139,7 +139,7 @@ onMounted(() => {
 
 <template>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="false"
     modal-label="Workflow Execution Parameters Modal"
   >
diff --git a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue
index 43f057f9103cf72bdc387ce8b0896f64542786c9..ea0a1d22b232ebe9b4fb9e4186dffb73e3e3a95c 100644
--- a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue
+++ b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue
@@ -7,6 +7,7 @@ import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import DeleteModal from "@/components/modals/DeleteModal.vue";
 import { GitRepository } from "@/utils/GitRepository";
 import { useWorkflowStore } from "@/stores/workflows";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const workflowRepository = useWorkflowStore();
 // Constants
@@ -131,31 +132,11 @@ onMounted(() => {
     :back-modal-id="modalID"
     @confirm-delete="deleteCredentials()"
   />
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div v-if="formState.updateCredentials" class="toast-body">
-          Successfully updated credentials
-        </div>
-        <div v-else class="toast-body">Successfully deleted credentials</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix">
+    Successfully updated credentials
+  </bootstrap-toast>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Update Workflow Version Icon Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue
index 1b393686f6bdd81ebcdf4ce82d6e975fcac7eaa4..2961a8d5db685984b352fc55fb49f81b66bc88de 100644
--- a/src/components/workflows/modals/UpdateWorkflowModal.vue
+++ b/src/components/workflows/modals/UpdateWorkflowModal.vue
@@ -21,6 +21,7 @@ import { valid, lte, inc } from "semver";
 import { latestVersion as calculateLatestVersion } from "@/utils/Workflow";
 import WorkflowModeTransitionGroup from "@/components/transitions/WorkflowModeTransitionGroup.vue";
 import { useWorkflowStore } from "@/stores/workflows";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const workflowRepository = useWorkflowStore();
 // Bootstrap Elements
@@ -307,28 +308,11 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">Successfully updated Workflow</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix">
+    Successfully updated Workflow
+  </bootstrap-toast>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Update Workflow Modal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
diff --git a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue
index ef529298b5b71f8f3fdb059a7d05801201b1c7a1..b820af5335b0021bfbd7538ce8e0c20d664e0d0c 100644
--- a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue
+++ b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue
@@ -6,6 +6,7 @@ import { Modal, Toast } from "bootstrap";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import DeleteModal from "@/components/modals/DeleteModal.vue";
 import { useWorkflowStore } from "@/stores/workflows";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const workflowRepository = useWorkflowStore();
 // Constants
@@ -140,31 +141,12 @@ onMounted(() => {
     :back-modal-id="modalID"
     @confirm-delete="deleteIcon()"
   />
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div v-if="formState.uploadIcon" class="toast-body">
-          Successfully uploaded icon
-        </div>
-        <div v-else class="toast-body">Successfully deleted icon</div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix">
+    <div v-if="formState.uploadIcon">Successfully uploaded icon</div>
+    <div v-else>Successfully deleted icon</div>
+  </bootstrap-toast>
   <bootstrap-modal
-    :modalID="modalID"
+    :modalId="modalID"
     :static-backdrop="true"
     modal-label="Update Workflow Version IconModal"
     v-on="{ 'hidden.bs.modal': modalClosed }"
diff --git a/src/environment.ts b/src/environment.ts
index cb756b7e77d18de3fe8208de0e8670b87e123a66..fd58108f1e1127b8178a2f11056323ea99e3a99f 100644
--- a/src/environment.ts
+++ b/src/environment.ts
@@ -6,6 +6,7 @@ export const environment: env = {
   S3PROXY_API_BASE_URL: windowEnv["s3proxyApiUrl"],
   AUTH_API_BASE_URL: windowEnv["authApiUrl"],
   WORKFLOW_API_BASE_URL: windowEnv["workflowApiUrl"],
+  RESOURCE_API_BASE_URL: windowEnv["resourceApiUrl"],
   DEV_SYSTEM: windowEnv["devSystem"].toLowerCase() === "true",
 };
 
@@ -14,5 +15,6 @@ type env = {
   S3PROXY_API_BASE_URL: string;
   AUTH_API_BASE_URL: string;
   WORKFLOW_API_BASE_URL: string;
+  RESOURCE_API_BASE_URL: string;
   DEV_SYSTEM: boolean;
 };
diff --git a/src/router/adminRoutes.ts b/src/router/adminRoutes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a7a4384781a425879efc121e17f1c2ea24253195
--- /dev/null
+++ b/src/router/adminRoutes.ts
@@ -0,0 +1,12 @@
+import type { RouteRecordRaw } from "vue-router";
+
+export const adminRoutes: RouteRecordRaw[] = [
+  {
+    path: "admin/resources",
+    name: "admin-resources",
+    component: () => import("../views/admin/AdminResourcesView.vue"),
+    meta: {
+      requiresAdminRole: true,
+    },
+  },
+];
diff --git a/src/router/index.ts b/src/router/index.ts
index 3f43bacab4caa4623ac235782926875e579273c3..21ae88be87f65a9832c6fcdbb7b83d8dc62a6214 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,6 +1,10 @@
 import { createRouter, createWebHistory } from "vue-router";
 import DashboardView from "../views/DashboardView.vue";
 import LoginView from "../views/LoginView.vue";
+import { workflowRoutes } from "@/router/workflowRoutes";
+import { s3Routes } from "@/router/s3Routes";
+import { resourceRoutes } from "@/router/resourceRoutes";
+import { adminRoutes } from "@/router/adminRoutes";
 
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
@@ -10,102 +14,10 @@ const router = createRouter({
       name: "dashboard",
       component: DashboardView,
       children: [
-        {
-          path: "object-storage/buckets",
-          name: "buckets",
-          component: () => import("../views/object-storage/BucketsView.vue"),
-          props: (route) => ({
-            bucketName: route.params.bucketName ?? undefined,
-          }),
-          children: [
-            {
-              path: ":bucketName/:subFolders*",
-              name: "bucket",
-              component: () => import("../views/object-storage/BucketView.vue"),
-              props: true,
-            },
-          ],
-        },
-        {
-          path: "object-storage/s3-keys",
-          name: "s3_keys",
-          component: () => import("../views/object-storage/S3KeysView.vue"),
-        },
-        {
-          path: "workflow-executions",
-          name: "workflow-executions",
-          component: () =>
-            import("../views/workflows/ListWorkflowExecutionsView.vue"),
-        },
-        {
-          path: "workflows",
-          name: "workflows",
-          component: () => import("../views/workflows/ListWorkflowsView.vue"),
-        },
-        {
-          path: "developer/workflows",
-          name: "workflows-developer",
-          component: () => import("../views/workflows/MyWorkflowsView.vue"),
-          meta: {
-            requiresDeveloperRole: true,
-          },
-        },
-        {
-          path: "reviewer/workflows",
-          name: "workflows-reviewer",
-          component: () => import("../views/workflows/ReviewWorkflowsView.vue"),
-          meta: {
-            requiresReviewerRole: true,
-          },
-        },
-        {
-          path: "workflows/arbitrary",
-          name: "arbitrary-workflow",
-          component: () =>
-            import("../views/workflows/ArbitraryWorkflowView.vue"),
-          meta: {
-            requiresDeveloperRole: true,
-          },
-          props: (route) => ({
-            wid: route.query.wid,
-          }),
-        },
-        {
-          path: "workflows/:workflowId",
-          name: "workflow",
-          component: () => import("../views/workflows/WorkflowView.vue"),
-          props: (route) => ({
-            versionId: route.params.versionId ?? undefined,
-            workflowId: route.params.workflowId,
-            workflowModeId: route.query.workflowModeId ?? undefined,
-            developerView: route.query.developerView == "true" ?? false,
-          }),
-          children: [
-            {
-              path: "version/:versionId",
-              name: "workflow-version",
-              component: () =>
-                import("../views/workflows/WorkflowVersionView.vue"),
-              props: (route) => ({
-                versionId: route.params.versionId,
-                workflowId: route.params.workflowId,
-                activeTab: route.query.tab ?? "description",
-                workflowModeId: route.query.workflowModeId ?? undefined,
-              }),
-            },
-            {
-              path: "version/:versionId/start",
-              name: "workflow-start",
-              component: () =>
-                import("../views/workflows/StartWorkflowView.vue"),
-              props: (route) => ({
-                versionId: route.params.versionId,
-                workflowId: route.params.workflowId,
-                workflowModeId: route.query.workflowModeId ?? undefined,
-              }),
-            },
-          ],
-        },
+        ...resourceRoutes,
+        ...s3Routes,
+        ...workflowRoutes,
+        ...adminRoutes,
       ],
     },
     {
diff --git a/src/router/resourceRoutes.ts b/src/router/resourceRoutes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..69cd0cffc0282ff9dc6063bc6bd47e551bd50178
--- /dev/null
+++ b/src/router/resourceRoutes.ts
@@ -0,0 +1,25 @@
+import type { RouteRecordRaw } from "vue-router";
+
+export const resourceRoutes: RouteRecordRaw[] = [
+  {
+    path: "resources",
+    name: "resources",
+    component: () => import("../views/resources/ListResourcesView.vue"),
+  },
+  {
+    path: "maintainer/resources",
+    name: "resource-maintainer",
+    component: () => import("../views/resources/MyResourcesView.vue"),
+    meta: {
+      requiresMaintainerRole: true,
+    },
+  },
+  {
+    path: "reviewer/resources",
+    name: "resource-review",
+    component: () => import("../views/resources/ReviewResourceView.vue"),
+    meta: {
+      requiresReviewerRole: true,
+    },
+  },
+];
diff --git a/src/router/s3Routes.ts b/src/router/s3Routes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..30ff3e0799e723c28e0c9dc61d78ea3b503680cb
--- /dev/null
+++ b/src/router/s3Routes.ts
@@ -0,0 +1,25 @@
+import type { RouteRecordRaw } from "vue-router";
+
+export const s3Routes: RouteRecordRaw[] = [
+  {
+    path: "object-storage/buckets",
+    name: "buckets",
+    component: () => import("../views/object-storage/BucketsView.vue"),
+    props: (route) => ({
+      bucketName: route.params.bucketName ?? undefined,
+    }),
+    children: [
+      {
+        path: ":bucketName/:subFolders*",
+        name: "bucket",
+        component: () => import("../views/object-storage/BucketView.vue"),
+        props: true,
+      },
+    ],
+  },
+  {
+    path: "object-storage/s3-keys",
+    name: "s3_keys",
+    component: () => import("../views/object-storage/S3KeysView.vue"),
+  },
+];
diff --git a/src/router/workflowRoutes.ts b/src/router/workflowRoutes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9d53737130d2766e62b44667122b634c71713747
--- /dev/null
+++ b/src/router/workflowRoutes.ts
@@ -0,0 +1,76 @@
+import type { RouteRecordRaw } from "vue-router";
+
+export const workflowRoutes: RouteRecordRaw[] = [
+  {
+    path: "workflow-executions",
+    name: "workflow-executions",
+    component: () =>
+      import("../views/workflows/ListWorkflowExecutionsView.vue"),
+  },
+  {
+    path: "workflows",
+    name: "workflows",
+    component: () => import("../views/workflows/ListWorkflowsView.vue"),
+  },
+  {
+    path: "developer/workflows",
+    name: "workflows-developer",
+    component: () => import("../views/workflows/MyWorkflowsView.vue"),
+    meta: {
+      requiresDeveloperRole: true,
+    },
+  },
+  {
+    path: "reviewer/workflows",
+    name: "workflows-reviewer",
+    component: () => import("../views/workflows/ReviewWorkflowsView.vue"),
+    meta: {
+      requiresReviewerRole: true,
+    },
+  },
+  {
+    path: "workflows/arbitrary",
+    name: "arbitrary-workflow",
+    component: () => import("../views/workflows/ArbitraryWorkflowView.vue"),
+    meta: {
+      requiresDeveloperRole: true,
+    },
+    props: (route) => ({
+      wid: route.query.wid,
+    }),
+  },
+  {
+    path: "workflows/:workflowId",
+    name: "workflow",
+    component: () => import("../views/workflows/WorkflowView.vue"),
+    props: (route) => ({
+      versionId: route.params.versionId ?? undefined,
+      workflowId: route.params.workflowId,
+      workflowModeId: route.query.workflowModeId ?? undefined,
+      developerView: route.query.developerView == "true",
+    }),
+    children: [
+      {
+        path: "version/:versionId",
+        name: "workflow-version",
+        component: () => import("../views/workflows/WorkflowVersionView.vue"),
+        props: (route) => ({
+          versionId: route.params.versionId,
+          workflowId: route.params.workflowId,
+          activeTab: route.query.tab ?? "description",
+          workflowModeId: route.query.workflowModeId ?? undefined,
+        }),
+      },
+      {
+        path: "version/:versionId/start",
+        name: "workflow-start",
+        component: () => import("../views/workflows/StartWorkflowView.vue"),
+        props: (route) => ({
+          versionId: route.params.versionId,
+          workflowId: route.params.workflowId,
+          workflowModeId: route.query.workflowModeId ?? undefined,
+        }),
+      },
+    ],
+  },
+];
diff --git a/src/stores/names.ts b/src/stores/names.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e2b76aa70e58f926db013beb7b5f106950fd6cdd
--- /dev/null
+++ b/src/stores/names.ts
@@ -0,0 +1,45 @@
+import { defineStore } from "pinia";
+
+export const useNameStore = defineStore({
+  id: "names",
+  state: () =>
+    ({
+      nameMapping: {},
+    }) as {
+      nameMapping: Record<string, string>;
+    },
+  getters: {
+    getName(): (objectID?: string) => string | undefined {
+      return (objectID) => {
+        if (objectID) {
+          return this.nameMapping[objectID] ?? localStorage.getItem(objectID);
+        }
+        return undefined;
+      };
+    },
+  },
+  actions: {
+    addNameToMapping(objectId: string, objectName: string) {
+      this.nameMapping[objectId] = objectName;
+      localStorage.setItem(objectId, objectName);
+    },
+    deleteNameFromMapping(objectId: string) {
+      delete this.nameMapping[objectId];
+      localStorage.removeItem(objectId);
+    },
+    loadNameMapping() {
+      if (Object.keys(this.nameMapping).length > 0) {
+        return;
+      }
+      for (let i = 0; i < localStorage.length; i++) {
+        const key = localStorage.key(i);
+        if (key != null) {
+          const value = localStorage.getItem(key);
+          if (value != null) {
+            this.nameMapping[key] = value;
+          }
+        }
+      }
+    },
+  },
+});
diff --git a/src/stores/resources.ts b/src/stores/resources.ts
new file mode 100644
index 0000000000000000000000000000000000000000..03f153a607db56a037c47928699ce8ca18dc643d
--- /dev/null
+++ b/src/stores/resources.ts
@@ -0,0 +1,272 @@
+import { defineStore } from "pinia";
+import type {
+  ResourceIn,
+  ResourceOut,
+  ResourceVersionIn,
+  ResourceVersionOut,
+} from "@/client/resource";
+import {
+  ResourceService,
+  ResourceVersionService,
+  Status,
+} from "@/client/resource";
+import { useAuthStore } from "@/stores/users";
+import { useNameStore } from "@/stores/names";
+
+export const useResourceStore = defineStore({
+  id: "resources",
+  state: () =>
+    ({
+      resourceMapping: {},
+      ownResourceMapping: {},
+      reviewableResourceMapping: {},
+    }) as {
+      resourceMapping: Record<string, ResourceOut>;
+      ownResourceMapping: Record<string, ResourceOut>;
+      reviewableResourceMapping: Record<string, ResourceOut>;
+    },
+  getters: {
+    resources(): ResourceOut[] {
+      return Object.values(this.resourceMapping);
+    },
+    ownResources(): ResourceOut[] {
+      return Object.values(this.ownResourceMapping);
+    },
+    reviewableResources(): ResourceOut[] {
+      return Object.values(this.reviewableResourceMapping);
+    },
+  },
+  actions: {
+    fetchResource(
+      resource_id: string,
+      versionStatus?: Status[],
+    ): Promise<ResourceOut> {
+      return ResourceService.resourceGetResource(
+        resource_id,
+        versionStatus,
+      ).then((resource) => {
+        const nameStore = useNameStore();
+        nameStore.addNameToMapping(resource.resource_id, resource.name);
+        for (const version of resource.versions) {
+          nameStore.addNameToMapping(
+            version.resource_version_id,
+            version.release,
+          );
+        }
+        return resource;
+      });
+    },
+    fetchResources(
+      maintainerId?: string,
+      versionStatus?: Status[],
+      searchString?: string,
+    ): Promise<ResourceOut[]> {
+      return ResourceService.resourceListResources(
+        maintainerId,
+        versionStatus,
+        searchString,
+      ).then((resources) => {
+        const nameStore = useNameStore();
+        for (const resource of resources) {
+          nameStore.addNameToMapping(resource.resource_id, resource.name);
+          for (const version of resource.versions) {
+            nameStore.addNameToMapping(
+              version.resource_version_id,
+              version.release,
+            );
+          }
+        }
+        return resources;
+      });
+    },
+    fetchReviewableResources(onFinally?: () => void): Promise<ResourceOut[]> {
+      if (Object.keys(this.reviewableResourceMapping).length > 0) {
+        onFinally?.();
+      }
+      return this.fetchResources(undefined, [
+        Status.SYNC_REQUESTED,
+        Status.SYNCHRONIZING,
+      ])
+        .then((resources) => {
+          const newMapping: Record<string, ResourceOut> = {};
+          for (const resource of resources) {
+            newMapping[resource.resource_id] = resource;
+          }
+          this.reviewableResourceMapping = newMapping;
+          return resources;
+        })
+        .finally(onFinally);
+    },
+    fetchPublicResources(onFinally?: () => void): Promise<ResourceOut[]> {
+      if (this.resources.length > 0) {
+        onFinally?.();
+      }
+      return this.fetchResources()
+        .then((resources) => {
+          const newMapping: Record<string, ResourceOut> = {};
+          for (const resource of resources) {
+            newMapping[resource.resource_id] = resource;
+          }
+          this.resourceMapping = newMapping;
+          return resources;
+        })
+        .finally(onFinally);
+    },
+    fetchOwnResource(
+      resource_id: string,
+      onFinally?: () => void,
+    ): Promise<ResourceOut> {
+      if (this.ownResourceMapping[resource_id]) {
+        onFinally?.();
+      }
+      return this.fetchResource(resource_id, Object.values(Status))
+        .then((resource) => {
+          this.ownResourceMapping[resource.resource_id] = resource;
+          return resource;
+        })
+        .finally(onFinally);
+    },
+    fetchOwnResources(onFinally?: () => void): Promise<ResourceOut[]> {
+      const authStore = useAuthStore();
+      if (this.ownResources.length > 0) {
+        onFinally?.();
+      }
+      return this.fetchResources(authStore.currentUID, Object.values(Status))
+        .then((resources) => {
+          const newMapping: Record<string, ResourceOut> = {};
+          for (const resource of resources) {
+            newMapping[resource.resource_id] = resource;
+          }
+          this.ownResourceMapping = newMapping;
+          return resources;
+        })
+        .finally(onFinally);
+    },
+    async createResource(resource: ResourceIn): Promise<ResourceOut> {
+      const createdResource =
+        await ResourceService.resourceCreateResource(resource);
+      this.ownResourceMapping[createdResource.resource_id] = createdResource;
+      const nameStore = useNameStore();
+      nameStore.addNameToMapping(
+        createdResource.resource_id,
+        createdResource.name,
+      );
+      nameStore.addNameToMapping(
+        createdResource.versions[0].resource_version_id,
+        createdResource.versions[0].release,
+      );
+
+      return createdResource;
+    },
+    requestSynchronization(
+      resourceVersion: ResourceVersionOut,
+    ): Promise<ResourceVersionOut> {
+      return ResourceVersionService.resourceVersionRequestResourceVersionSync(
+        resourceVersion.resource_id,
+        resourceVersion.resource_version_id,
+      ).then((changedResourceVersion) => {
+        if (
+          this.ownResourceMapping[changedResourceVersion.resource_id] ==
+          undefined
+        ) {
+          this.fetchOwnResource(resourceVersion.resource_id);
+          return changedResourceVersion;
+        }
+        const versionIndex = this.ownResourceMapping[
+          changedResourceVersion.resource_id
+        ].versions.findIndex(
+          (version) =>
+            version.resource_version_id ==
+            changedResourceVersion.resource_version_id,
+        );
+        if (versionIndex > -1) {
+          this.ownResourceMapping[changedResourceVersion.resource_id].versions[
+            versionIndex
+          ] = changedResourceVersion;
+        } else {
+          this.ownResourceMapping[
+            changedResourceVersion.resource_id
+          ].versions.push(changedResourceVersion);
+        }
+        return changedResourceVersion;
+      });
+    },
+    updateResource(
+      resource_id: string,
+      version: ResourceVersionIn,
+    ): Promise<ResourceVersionOut> {
+      return ResourceVersionService.resourceVersionRequestResourceVersion(
+        resource_id,
+        version,
+      ).then((versionOut) => {
+        if (this.ownResourceMapping[versionOut.resource_id] == undefined) {
+          this.fetchOwnResource(versionOut.resource_id);
+          return versionOut;
+        }
+        useNameStore().addNameToMapping(
+          versionOut.resource_version_id,
+          versionOut.release,
+        );
+        this.ownResourceMapping[versionOut.resource_id].versions.push(
+          versionOut,
+        );
+        return versionOut;
+      });
+    },
+    syncResource(
+      resourceVersion: ResourceVersionOut,
+    ): Promise<ResourceVersionOut> {
+      return ResourceVersionService.resourceVersionResourceVersionSync(
+        resourceVersion.resource_id,
+        resourceVersion.resource_version_id,
+      ).then((changedVersion) => {
+        if (
+          this.reviewableResourceMapping[changedVersion.resource_id] ==
+          undefined
+        ) {
+          return changedVersion;
+        }
+        const versionIndex = this.reviewableResourceMapping[
+          changedVersion.resource_id
+        ].versions.findIndex(
+          (version) =>
+            version.resource_version_id == changedVersion.resource_version_id,
+        );
+        if (versionIndex > -1) {
+          this.reviewableResourceMapping[changedVersion.resource_id].versions[
+            versionIndex
+          ] = changedVersion;
+        } else {
+          this.reviewableResourceMapping[
+            changedVersion.resource_id
+          ].versions.push(changedVersion);
+        }
+        return changedVersion;
+      });
+    },
+    setLatestResource(
+      resourceVersion: ResourceVersionOut,
+    ): Promise<ResourceVersionOut> {
+      return ResourceVersionService.resourceVersionResourceVersionLatest(
+        resourceVersion.resource_id,
+        resourceVersion.resource_version_id,
+      );
+    },
+    deleteOnCluster(
+      resourceVersion: ResourceVersionOut,
+    ): Promise<ResourceVersionOut> {
+      return ResourceVersionService.resourceVersionDeleteResourceVersionCluster(
+        resourceVersion.resource_id,
+        resourceVersion.resource_version_id,
+      );
+    },
+    deleteInS3(
+      resourceVersion: ResourceVersionOut,
+    ): Promise<ResourceVersionOut> {
+      return ResourceVersionService.resourceVersionDeleteResourceVersionS3(
+        resourceVersion.resource_id,
+        resourceVersion.resource_version_id,
+      );
+    },
+  },
+});
diff --git a/src/stores/users.ts b/src/stores/users.ts
index 37497e582784ef6f0fdebcdb2ce205aeb561dc10..928ed2956df6f5a9d322d219b329c4f0f1b59fe3 100644
--- a/src/stores/users.ts
+++ b/src/stores/users.ts
@@ -4,12 +4,14 @@ import { UserService, RoleEnum } from "@/client/auth";
 import { OpenAPI as S3ProxyOpenAPI } from "@/client/s3proxy";
 import { OpenAPI as AuthOpenAPI } from "@/client/auth";
 import { OpenAPI as WorkflowOpenAPI } from "@/client/workflow";
+import { OpenAPI as ResourceOpenAPI } from "@/client/resource";
 import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
 import { useBucketStore } from "@/stores/buckets";
 import { useWorkflowStore } from "@/stores/workflows";
 import { useS3KeyStore } from "@/stores/s3keys";
 import { useS3ObjectStore } from "@/stores/s3objects";
 import { clear as dbclear } from "idb-keyval";
+import { useNameStore } from "@/stores/names";
 
 type DecodedToken = {
   exp: number;
@@ -22,7 +24,6 @@ export type RootState = {
   token: string | null;
   decodedToken: DecodedToken | null;
   user: User | null;
-  userMapping: Record<string, string>;
 };
 
 function parseJwt(token: string): DecodedToken {
@@ -48,7 +49,6 @@ export const useAuthStore = defineStore({
       token: null,
       decodedToken: null,
       user: null,
-      userMapping: {},
     }) as RootState,
   getters: {
     roles(): string[] {
@@ -70,7 +70,7 @@ export const useAuthStore = defineStore({
       state.user?.roles?.includes(RoleEnum.USER) ??
       state.decodedToken?.roles.includes(RoleEnum.USER) ??
       false,
-    workflowReviewer: (state) =>
+    rewiewer: (state) =>
       state.user?.roles?.includes(RoleEnum.REVIEWER) ??
       state.decodedToken?.roles.includes(RoleEnum.REVIEWER) ??
       false,
@@ -78,6 +78,10 @@ export const useAuthStore = defineStore({
       state.user?.roles?.includes(RoleEnum.DEVELOPER) ??
       state.decodedToken?.roles.includes(RoleEnum.DEVELOPER) ??
       false,
+    resourceMaintainer: (state) =>
+      state.user?.roles?.includes(RoleEnum.DB_MAINTAINER) ??
+      state.decodedToken?.roles.includes(RoleEnum.DB_MAINTAINER) ??
+      false,
     admin: (state) =>
       state.user?.roles?.includes(RoleEnum.ADMINISTRATOR) ??
       state.decodedToken?.roles.includes(RoleEnum.ADMINISTRATOR) ??
@@ -91,6 +95,7 @@ export const useAuthStore = defineStore({
         S3ProxyOpenAPI.TOKEN = token;
         AuthOpenAPI.TOKEN = token;
         WorkflowOpenAPI.TOKEN = token;
+        ResourceOpenAPI.TOKEN = token;
         UserService.userGetLoggedInUser()
           .then((user) => {
             this.updateUser(user);
@@ -104,8 +109,13 @@ export const useAuthStore = defineStore({
     },
     updateUser(user: User) {
       this.user = user;
+      useNameStore().addNameToMapping(user.uid, user.display_name);
     },
     logout() {
+      S3ProxyOpenAPI.TOKEN = undefined;
+      AuthOpenAPI.TOKEN = undefined;
+      WorkflowOpenAPI.TOKEN = undefined;
+      ResourceOpenAPI.TOKEN = undefined;
       this.$reset();
       localStorage.clear();
       dbclear();
@@ -115,15 +125,10 @@ export const useAuthStore = defineStore({
       useS3KeyStore().$reset();
       useS3ObjectStore().$reset();
     },
-    async addUidToNameMapping(mapping: Record<string, string>): Promise<void> {
-      for (const uid of Object.keys(mapping)) {
-        this.userMapping[uid] = mapping[uid];
-        localStorage.setItem(uid, mapping[uid]);
-      }
-    },
     async fetchUsernames(uids: string[]): Promise<string[]> {
+      const nameStore = useNameStore();
       const filteredIds = uids
-        .filter((uid) => !this.userMapping[uid]) // filter already present UIDs
+        .filter((uid) => !nameStore.getName(uid)) // filter already present UIDs
         .filter(
           // filter unique UIDs
           (modeId, index, array) =>
@@ -131,7 +136,8 @@ export const useAuthStore = defineStore({
         );
       // If all uids are already in the store, then return them
       if (filteredIds.length === 0) {
-        return uids.map((uid) => this.userMapping[uid]);
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        return uids.map((uid) => nameStore.getName(uid)!);
       }
       const missingIds: string[] = [];
       const storedNames = filteredIds.map((uid) => localStorage.getItem(uid));
@@ -142,23 +148,19 @@ export const useAuthStore = defineStore({
           missingIds.push(filteredIds[index]);
         } else {
           // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-          this.userMapping[filteredIds[index]] = storedNames[index]!;
+          nameStore.addNameToMapping(filteredIds[index], storedNames[index]!);
         }
       }
-      // If all uids could be resolved from cache, return them
-      if (missingIds.length === 0) {
-        return uids.map((uid) => this.userMapping[uid]);
-      }
       // fetch missing users from backend
       const fetchedUsers = await Promise.all(
         missingIds.map((uid) => UserService.userGetUser(uid)),
       );
       // Put users in store
       for (const user of fetchedUsers) {
-        this.userMapping[user.uid] = user.display_name;
-        localStorage.setItem(user.uid, user.display_name);
+        nameStore.addNameToMapping(user.uid, user.display_name);
       }
-      return uids.map((uid) => this.userMapping[uid]);
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      return uids.map((uid) => nameStore.getName(uid)!);
     },
   },
 });
diff --git a/src/stores/workflows.ts b/src/stores/workflows.ts
index 6c7e04b3e2ba72d49ffed420cd41953e2a6e3b81..8b1bd2473e96e9b20d98fc0205edebe695a18af5 100644
--- a/src/stores/workflows.ts
+++ b/src/stores/workflows.ts
@@ -18,19 +18,17 @@ import {
 } from "@/client/workflow";
 import { useAuthStore } from "@/stores/users";
 import { set, get } from "idb-keyval";
+import { useNameStore } from "@/stores/names";
 
 export const useWorkflowStore = defineStore({
   id: "workflows",
   state: () =>
     ({
       workflowMapping: {},
-      nameMapping: {},
       comprehensiveWorkflowMapping: {},
       modeMapping: {},
-      modeNameMapping: {},
     }) as {
       workflowMapping: Record<string, WorkflowOut>;
-      nameMapping: Record<string, string>;
       comprehensiveWorkflowMapping: Record<string, WorkflowOut>;
       modeMapping: Record<string, WorkflowModeOut>;
     },
@@ -61,18 +59,13 @@ export const useWorkflowStore = defineStore({
       }
       return mapping;
     },
-    getName(): (objectID: string) => string | undefined {
-      return (objectID) =>
-        this.nameMapping[objectID] ?? localStorage.getItem(objectID);
-    },
     getArbitraryWorkflow(): (wid: string) => Promise<WorkflowIn | undefined> {
       return (wid: string) => get(wid);
     },
   },
   actions: {
     __addNameToMapping(key: string, value: string) {
-      this.nameMapping[key] = value;
-      localStorage.setItem(key, value);
+      useNameStore().addNameToMapping(key, value);
     },
     fetchWorkflows(onFinally?: () => void): Promise<WorkflowOut[]> {
       if (Object.keys(this.workflowMapping).length > 0) {
@@ -300,8 +293,7 @@ export const useWorkflowStore = defineStore({
     },
     deleteWorkflow(workflow_id: string): Promise<void> {
       return WorkflowService.workflowDeleteWorkflow(workflow_id).then(() => {
-        delete this.nameMapping[workflow_id];
-        localStorage.removeItem(workflow_id);
+        useNameStore().deleteNameFromMapping(workflow_id);
         delete this.workflowMapping[workflow_id];
         delete this.comprehensiveWorkflowMapping[workflow_id];
       });
diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue
index d9e1987acd5cf60c7093f05aee006c90f5fc6ffd..ef5a66c6935b0ac6055d59a3afe0c35d7d42b194 100644
--- a/src/views/LoginView.vue
+++ b/src/views/LoginView.vue
@@ -4,6 +4,7 @@ import { useAuthStore } from "@/stores/users";
 import { useRouter, useRoute } from "vue-router";
 import { OpenAPI as AuthOpenAPI } from "@/client/auth";
 import { Toast } from "bootstrap";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const router = useRouter();
 const route = useRoute();
@@ -29,35 +30,16 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-danger align-items-center border-0"
-      style="--bs-bg-opacity: 0.7"
-      data-bs-config="{'delay': 8000}"
-      data-bs-autohide="true"
-      id="loginErrorToast"
-    >
-      <div class="toast-header text-bg-danger">
-        <strong class="me-auto">Login Error</strong>
-        <button
-          type="button"
-          class="btn-close btn-close-white"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-      <div class="toast-body">
-        <p>
-          There has been some kind of error during the login.<br />
-          Please try again later.
-        </p>
-        <p>Error Code: {{ route.query.login_error }}</p>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast toast-id="loginErrorToast" color-class="danger">
+    <template> Login Error </template>
+    <template #body>
+      <p>
+        There has been some kind of error during the login.<br />
+        Please try again later.
+      </p>
+      <p>Error Code: {{ route.query.login_error }}</p>
+    </template>
+  </bootstrap-toast>
   <div class="position-fixed start-50 translate-middle-x text-center">
     <img
       src="/src/assets/images/clowm.svg"
diff --git a/src/views/admin/AdminResourcesView.vue b/src/views/admin/AdminResourcesView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..9ff663eced269ab6edb8468a8849e5c3a5eafb16
--- /dev/null
+++ b/src/views/admin/AdminResourcesView.vue
@@ -0,0 +1,351 @@
+<script setup lang="ts">
+import { useResourceStore } from "@/stores/resources";
+import { reactive } from "vue";
+import {
+  type ResourceOut,
+  type ResourceVersionOut,
+  Status,
+} from "@/client/resource";
+import SearchUserModal from "@/components/modals/SearchUserModal.vue";
+import type { User } from "@/client/auth";
+import { useNameStore } from "@/stores/names";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import dayjs from "dayjs";
+import ResourceVersionInfoModal from "@/components/resources/ResourceVersionInfoModal.vue";
+
+const resourceRepository = useResourceStore();
+const nameRepository = useNameStore();
+
+const resourceState = reactive<{
+  loading: boolean;
+  resources: ResourceOut[];
+  maintainerId: string;
+  searchString: string;
+  resourceStatus: Status[];
+  inspectVersionIndex: number;
+  inspectResource?: ResourceOut;
+}>({
+  loading: false,
+  resources: [],
+  maintainerId: "",
+  searchString: "",
+  resourceStatus: [],
+  inspectVersionIndex: 0,
+  inspectResource: undefined,
+});
+
+function updateUser(user: User) {
+  resourceState.maintainerId = user.uid;
+}
+
+function searchResources() {
+  resourceState.loading = true;
+  resourceRepository
+    .fetchResources(
+      resourceState.maintainerId ? resourceState.maintainerId : undefined,
+      resourceState.resourceStatus ? resourceState.resourceStatus : undefined,
+      resourceState.searchString ? resourceState.searchString : undefined,
+    )
+    .then((resources) => {
+      resourceState.resources = resources;
+    })
+    .finally(() => {
+      resourceState.loading = false;
+    });
+}
+
+function replaceResourceVersion(
+  resourceVersion: ResourceVersionOut,
+): ResourceVersionOut {
+  const resourceIndex = resourceState.resources.findIndex(
+    (r) => r.resource_id == resourceVersion.resource_id,
+  );
+  if (resourceIndex > -1) {
+    const versionIndex = resourceState.resources[
+      resourceIndex
+    ].versions.findIndex(
+      (r) => r.resource_version_id == resourceVersion.resource_version_id,
+    );
+    if (versionIndex > -1) {
+      resourceState.resources[resourceIndex].versions[versionIndex] =
+        resourceVersion;
+    }
+  }
+  return resourceVersion;
+}
+
+function setLatest(resourceVersion: ResourceVersionOut) {
+  resourceState.loading = true;
+  resourceRepository
+    .setLatestResource(resourceVersion)
+    .then(replaceResourceVersion)
+    .finally(() => {
+      resourceState.loading = false;
+    });
+}
+
+function deleteOnCluster(resourceVersion: ResourceVersionOut) {
+  resourceState.loading = true;
+  resourceRepository
+    .deleteOnCluster(resourceVersion)
+    .then(replaceResourceVersion)
+    .finally(() => {
+      resourceState.loading = false;
+    });
+}
+
+function deleteInS3(resourceVersion: ResourceVersionOut) {
+  resourceState.loading = true;
+  resourceRepository
+    .deleteInS3(resourceVersion)
+    .then(replaceResourceVersion)
+    .finally(() => {
+      resourceState.loading = false;
+    });
+}
+
+function syncToCluster(resourceVersion: ResourceVersionOut) {
+  resourceState.loading = true;
+  resourceRepository
+    .syncResource(resourceVersion)
+    .then(replaceResourceVersion)
+    .finally(() => {
+      resourceState.loading = false;
+    });
+}
+
+function resetForm() {
+  resourceState.maintainerId = "";
+  resourceState.searchString = "";
+  resourceState.resourceStatus = [];
+  resourceState.resources = [];
+}
+</script>
+
+<template>
+  <search-user-modal
+    modal-id="admin-resource-search-user-modal"
+    @user-found="updateUser"
+  />
+  <resource-version-info-modal
+    modal-id="admin-resource-version-info-modal"
+    :resource-version-index="resourceState.inspectVersionIndex"
+    :resource="resourceState.inspectResource"
+  />
+  <div
+    class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
+  >
+    <h2>Manage Resources</h2>
+  </div>
+  <form @submit.prevent="searchResources" id="admin-resource-search-form">
+    <div class="d-flex justify-content-evenly align-content-center">
+      <div class="mx-2">
+        <label for="admin-resource-state-select" class="form-label"
+          >Status of Resource Versions</label
+        >
+        <select
+          v-model="resourceState.resourceStatus"
+          multiple
+          class="form-select mb-4 w-fit"
+          id="admin-resource-state-select"
+        >
+          <option v-for="state in Object.values(Status)" :key="state">
+            {{ state }}
+          </option>
+        </select>
+      </div>
+      <div class="flex-fill mx-2">
+        <label for="admin-resource-name-search" class="form-label"
+          >Name of the Resource</label
+        >
+        <div class="input-group">
+          <input
+            id="admin-resource-name-search"
+            type="text"
+            class="form-control"
+            maxlength="32"
+            v-model="resourceState.searchString"
+            placeholder="Search for resource name"
+          />
+        </div>
+      </div>
+      <div class="flex-fill mx-2">
+        <label for="admin-resource-user-search" class="form-label"
+          >Name of the Maintainer</label
+        >
+        <div class="input-group">
+          <div class="input-group-text">
+            <font-awesome-icon icon="fa-solid fa-user" />
+          </div>
+          <input
+            id="admin-resource-user-search"
+            type="text"
+            class="form-control"
+            readonly
+            :value="nameRepository.getName(resourceState.maintainerId)"
+            placeholder="Search for maintainer"
+            data-bs-toggle="modal"
+            data-bs-target="#admin-resource-search-user-modal"
+          />
+        </div>
+      </div>
+    </div>
+    <button
+      type="submit"
+      class="btn btn-primary w-fit"
+      :disabled="resourceState.loading"
+    >
+      Search
+    </button>
+    <button
+      type="button"
+      class="btn-primary btn w-fit ms-4"
+      :disabled="resourceState.loading"
+      @click="resetForm"
+    >
+      Reset
+    </button>
+  </form>
+  <table class="table table-striped" v-if="resourceState.resources">
+    <thead>
+      <tr>
+        <th scope="col"><b>Resource ID</b></th>
+        <th scope="col">Name</th>
+        <th scope="col">Source</th>
+        <th scope="col">Maintainer</th>
+      </tr>
+    </thead>
+    <tbody v-if="resourceState.resources.length === 0">
+      <tr>
+        <td colspan="5" class="text-center fst-italic fw-light">
+          Select a filter and search for Resources
+        </td>
+      </tr>
+    </tbody>
+    <tbody v-else>
+      <template
+        v-for="resource in resourceState.resources"
+        :key="resource.resource_id"
+      >
+        <tr>
+          <th scope="row">{{ resource.resource_id }}</th>
+          <td>{{ resource.name }}</td>
+          <td>{{ resource.source }}</td>
+          <td>{{ nameRepository.getName(resource.maintainer_id) }}</td>
+        </tr>
+        <tr>
+          <td colspan="5">
+            <table class="table mb-0 table-hover">
+              <thead>
+                <tr>
+                  <th scope="col"><b>Version ID</b></th>
+                  <th scope="col">Release</th>
+                  <th scope="col">Created At</th>
+                  <th scope="col">State</th>
+                  <th scope="col" class="text-end">Action</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr
+                  v-for="(version, index) in resource.versions"
+                  :key="version.resource_version_id"
+                >
+                  <th scope="row">{{ version.resource_version_id }}</th>
+                  <th>{{ version.release }}</th>
+                  <th>
+                    {{
+                      dayjs
+                        .unix(version.created_at)
+                        .format("DD.MM.YYYY HH:mm:ss")
+                    }}
+                  </th>
+                  <th>{{ version.status }}</th>
+                  <th class="text-end">
+                    <div class="btn-group">
+                      <button
+                        type="button"
+                        class="btn btn-secondary"
+                        data-bs-toggle="modal"
+                        data-bs-target="#admin-resource-version-info-modal"
+                        @click="
+                          resourceState.inspectResource = resource;
+                          resourceState.inspectVersionIndex = index;
+                        "
+                      >
+                        Inspect
+                      </button>
+                      <button
+                        type="button"
+                        class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
+                        data-bs-toggle="dropdown"
+                        aria-expanded="false"
+                        :disabled="resourceState.loading"
+                      >
+                        <span class="visually-hidden">Toggle Dropdown</span>
+                      </button>
+                      <ul class="dropdown-menu">
+                        <li v-if="version.status === Status.SYNCHRONIZED">
+                          <button
+                            class="dropdown-item"
+                            type="button"
+                            @click="setLatest(version)"
+                          >
+                            Set to Latest
+                          </button>
+                        </li>
+                        <li
+                          v-if="
+                            version.status === Status.CLUSTER_DELETED ||
+                            version.status === Status.SYNC_REQUESTED
+                          "
+                        >
+                          <button
+                            class="dropdown-item"
+                            type="button"
+                            @click="syncToCluster(version)"
+                          >
+                            <font-awesome-icon
+                              icon="fa-solid fa-cloud-arrow-down"
+                            />
+                            <span class="ms-1">Sync to Cluster</span>
+                          </button>
+                        </li>
+                        <li
+                          v-if="
+                            version.status === Status.SYNCHRONIZED ||
+                            version.status === Status.LATEST
+                          "
+                        >
+                          <button
+                            class="dropdown-item text-danger align-middle"
+                            type="button"
+                            @click="deleteOnCluster(version)"
+                          >
+                            <font-awesome-icon icon="fa-solid fa-trash" />
+                            <span class="ms-1">Delete on Cluster</span>
+                          </button>
+                        </li>
+                        <li v-if="version.status === Status.CLUSTER_DELETED">
+                          <button
+                            class="dropdown-item text-danger align-middle"
+                            type="button"
+                            @click="deleteInS3(version)"
+                          >
+                            <font-awesome-icon icon="fa-solid fa-trash" />
+                            <span class="ms-1">Delete in S3</span>
+                          </button>
+                        </li>
+                      </ul>
+                    </div>
+                  </th>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </template>
+    </tbody>
+  </table>
+</template>
+
+<style scoped></style>
diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue
index b3bd3768dc2e75d1d7607400d5a777f9487cc8eb..9f43cbc44fa1de1ed93971a2d578161526b85e18 100644
--- a/src/views/object-storage/BucketView.vue
+++ b/src/views/object-storage/BucketView.vue
@@ -21,6 +21,7 @@ import { useAuthStore } from "@/stores/users";
 import { useBucketStore } from "@/stores/buckets";
 import { useS3ObjectStore } from "@/stores/s3objects";
 import { useS3KeyStore } from "@/stores/s3keys";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const authStore = useAuthStore();
 const bucketRepository = useBucketStore();
@@ -420,28 +421,9 @@ function getObjectFileName(key: string): string {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successToast-' + randomIDSuffix"
-    >
-      <div class="d-flex">
-        <div class="toast-body">
-          Successfully deleted {{ deleteObjectsState.deletedItem }}
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast :toast-id="'successToast-' + randomIDSuffix">
+    Successfully deleted {{ deleteObjectsState.deletedItem }}
+  </bootstrap-toast>
   <DeleteModal
     modalID="delete-object-modal"
     :object-name-delete="deleteObjectsState.potentialObjectToDelete"
diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue
index 12218064e3e4ca66cc8c1d832b4fcf47033cf9cd..1735cdb1c8d1a67448ea932770c6c5eac42fa6d2 100644
--- a/src/views/object-storage/S3KeysView.vue
+++ b/src/views/object-storage/S3KeysView.vue
@@ -5,6 +5,7 @@ import { reactive, onMounted, computed } from "vue";
 import { useAuthStore } from "@/stores/users";
 import { Toast, Tooltip } from "bootstrap";
 import { useS3KeyStore } from "@/stores/s3keys";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const authStore = useAuthStore();
 const keyRepository = useS3KeyStore();
@@ -76,28 +77,9 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-success align-items-center border-0"
-      data-bs-autohide="true"
-      :id="'successKeyToast'"
-    >
-      <div class="d-flex">
-        <div class="toast-body">
-          Successfully deleted S3 Key {{ keyState.deletedKey.slice(0, 5) }}...
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white me-2 m-auto"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast toast-id="successKeyToast">
+    Successfully deleted S3 Key {{ keyState.deletedKey.slice(0, 5) }}...
+  </bootstrap-toast>
   <div class="row m-2 border-bottom mt-4">
     <div class="col-12"></div>
     <h2 class="mb-2">S3 Keys</h2>
diff --git a/src/views/resources/ListResourcesView.vue b/src/views/resources/ListResourcesView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b615344d6aadb6e6602fd47a984953b9f3e6e8b7
--- /dev/null
+++ b/src/views/resources/ListResourcesView.vue
@@ -0,0 +1,149 @@
+<script setup lang="ts">
+import { computed, onMounted, reactive } from "vue";
+import { useResourceStore } from "@/stores/resources";
+import ResourceCard from "@/components/resources/ResourceCard.vue";
+import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
+import { useAuthStore } from "@/stores/users";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import type { ResourceOut } from "@/client/resource";
+
+const resourceRepository = useResourceStore();
+const userRepository = useAuthStore();
+
+const resourceState = reactive<{
+  loading: boolean;
+  filterString: string;
+  sortDesc: boolean;
+}>({
+  loading: true,
+  filterString: "",
+  sortDesc: true,
+});
+
+const sortedResourcesByName = computed<ResourceOut[]>(() => {
+  return [...resourceRepository.resources].sort((a, b) =>
+    a.name < b.name ? 1 : -1,
+  );
+});
+
+const sortedResources = computed<ResourceOut[]>(() => {
+  if (resourceState.sortDesc) {
+    return [...sortedResourcesByName.value].reverse();
+  }
+  return sortedResourcesByName.value;
+});
+
+const filteredSortedResources = computed<ResourceOut[]>(() => {
+  return sortedResources.value.filter((resource) => {
+    return resourceState.filterString.length > 0
+      ? resource.name.includes(resourceState.filterString)
+      : true;
+  });
+});
+
+onMounted(() => {
+  resourceRepository
+    .fetchPublicResources(() => {
+      resourceState.loading = false;
+    })
+    .then((resources) => {
+      userRepository.fetchUsernames(resources.map((r) => r.maintainer_id));
+    });
+});
+</script>
+
+<template>
+  <div class="row m-2 border-bottom mb-4">
+    <h2 class="mb-2">Available Resources</h2>
+  </div>
+  <div class="d-flex m-2 mb-3 align-items-center justify-content-start">
+    <div class="col-5">
+      <div class="input-group rounded shadow-sm">
+        <span class="input-group-text" id="resources-search-wrapping"
+          ><font-awesome-icon icon="fa-solid fa-magnifying-glass"
+        /></span>
+        <input
+          type="text"
+          id="filterResourcesInput"
+          class="form-control"
+          placeholder="Filter Resources"
+          aria-label="Filter Resources"
+          aria-describedby="resources-search-wrapping"
+          :disabled="resourceState.loading"
+          v-model.trim="resourceState.filterString"
+          maxlength="20"
+        />
+      </div>
+    </div>
+    <font-awesome-icon
+      :icon="
+        resourceState.sortDesc
+          ? 'fa-solid fa-arrow-down-wide-short'
+          : 'fa-solid fa-arrow-up-wide-short'
+      "
+      @click="resourceState.sortDesc = !resourceState.sortDesc"
+      class="fs-5 ms-3 cursor-pointer"
+    />
+  </div>
+  <div v-if="!resourceState.loading">
+    <div
+      v-if="resourceRepository.resources.length === 0"
+      class="text-center fs-2 mt-5"
+    >
+      <font-awesome-icon
+        icon="fa-solid fa-x"
+        class="my-5 fs-0"
+        style="color: var(--bs-secondary)"
+      />
+      <p>There are no resources in the system. Please come again later.</p>
+    </div>
+    <div
+      v-else-if="filteredSortedResources.length === 0"
+      class="text-center fs-2 mt-5"
+    >
+      <font-awesome-icon
+        icon="fa-solid fa-magnifying-glass"
+        class="my-5 fs-0"
+        style="color: var(--bs-secondary)"
+      />
+      <p>
+        Could not find any Resources containing<br />'{{
+          resourceState.filterString
+        }}'
+      </p>
+    </div>
+    <CardTransitionGroup
+      v-else
+      class="d-flex flex-wrap align-items-center justify-content-between"
+    >
+      <resource-card
+        v-for="resource in filteredSortedResources"
+        :key="resource.resource_id"
+        :resource="resource"
+        :loading="false"
+        style="min-width: 48%"
+      />
+    </CardTransitionGroup>
+  </div>
+  <div
+    v-else
+    class="d-flex flex-wrap align-items-center justify-content-between"
+  >
+    <resource-card
+      v-for="resource in 4"
+      :key="resource"
+      :resource="{
+        name: '',
+        description: '',
+        source: '',
+        resource_id: '',
+        versions: [],
+        maintainer_id: '',
+      }"
+      style="min-width: 48%"
+      loading
+    />
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/src/views/resources/MyResourcesView.vue b/src/views/resources/MyResourcesView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e9a3d231e44cd6288bf83e748d2dfe9b7bf37a61
--- /dev/null
+++ b/src/views/resources/MyResourcesView.vue
@@ -0,0 +1,137 @@
+<script setup lang="ts">
+import { onMounted, reactive } from "vue";
+import { useResourceStore } from "@/stores/resources";
+import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
+import ResourceCard from "@/components/resources/ResourceCard.vue";
+import CreateResourceModal from "@/components/resources/modals/CreateResourceModal.vue";
+import UploadResourceInfoModal from "@/components/resources/modals/UploadResourceInfoModal.vue";
+import { useS3KeyStore } from "@/stores/s3keys";
+import type { ResourceVersionOut, ResourceOut } from "@/client/resource";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import UpdateResourceModal from "@/components/resources/modals/UpdateResourceModal.vue";
+
+const resourceRepository = useResourceStore();
+const s3KeyRepository = useS3KeyStore();
+
+const resourceState = reactive<{
+  loading: boolean;
+  resourceVersionInfo?: ResourceVersionOut;
+  updateResource: ResourceOut;
+}>({
+  loading: true,
+  resourceVersionInfo: undefined,
+  updateResource: {
+    name: "",
+    description: "",
+    source: "",
+    resource_id: "",
+    versions: [],
+    maintainer_id: "",
+  },
+});
+
+function setResourceVersionInfo(resourceVersionInfo?: ResourceVersionOut) {
+  resourceState.resourceVersionInfo = resourceVersionInfo;
+}
+
+function setResourceUpdate(resource: ResourceOut) {
+  resourceState.updateResource = resource;
+}
+
+onMounted(() => {
+  let fetchedResources = false;
+  s3KeyRepository.fetchS3Keys(() => {
+    if (!fetchedResources) {
+      fetchedResources = true;
+      resourceRepository.fetchOwnResources(() => {
+        resourceState.loading = false;
+      });
+    }
+  });
+});
+</script>
+
+<template>
+  <create-resource-modal modal-i-d="createResourceModal" />
+  <upload-resource-info-modal
+    modal-id="uploadResourceInfoModal"
+    :resource-version="resourceState.resourceVersionInfo"
+  />
+  <update-resource-modal
+    :resource="resourceState.updateResource"
+    modal-id="updateResourceModal"
+  />
+  <div
+    class="row m-2 border-bottom mb-4 justify-content-between align-items-center pb-2"
+  >
+    <h2 class="w-fit">My Resources</h2>
+    <div class="w-fit">
+      <button
+        type="button"
+        class="btn btn-info btn-lg w-fit me-3"
+        data-bs-toggle="modal"
+        data-bs-target="#uploadResourceInfoModal"
+        @click="setResourceVersionInfo(undefined)"
+      >
+        Tutorial
+      </button>
+      <button
+        class="btn btn-lg btn-primary w-fit"
+        data-bs-toggle="modal"
+        data-bs-target="#createResourceModal"
+      >
+        Create Resource
+      </button>
+    </div>
+  </div>
+  <div v-if="!resourceState.loading">
+    <div
+      v-if="resourceRepository.ownResources.length === 0"
+      class="text-center fs-2 mt-5"
+    >
+      <font-awesome-icon
+        icon="fa-solid fa-x"
+        class="my-5 fs-0"
+        style="color: var(--bs-secondary)"
+      />
+      <p>You don't have any resources registered in the system.</p>
+    </div>
+    <CardTransitionGroup
+      v-else
+      class="d-flex flex-wrap align-items-center justify-content-between"
+    >
+      <resource-card
+        v-for="resource in resourceRepository.ownResources"
+        :key="resource.resource_id"
+        :resource="resource"
+        :loading="false"
+        style="width: 48%"
+        extended
+        @click-info="setResourceVersionInfo"
+        @click-update="setResourceUpdate"
+      />
+    </CardTransitionGroup>
+  </div>
+  <div
+    v-else
+    class="d-flex flex-wrap align-items-center justify-content-between"
+  >
+    <resource-card
+      v-for="resource in 4"
+      :key="resource"
+      :resource="{
+        name: '',
+        description: '',
+        source: '',
+        resource_id: '',
+        versions: [],
+        maintainer_id: '',
+      }"
+      style="min-width: 48%"
+      loading
+      extended
+    />
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/src/views/resources/ReviewResourceView.vue b/src/views/resources/ReviewResourceView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4e9a33a08a34f0fe619d57d0e1ca129f19e75d14
--- /dev/null
+++ b/src/views/resources/ReviewResourceView.vue
@@ -0,0 +1,186 @@
+<script setup lang="ts">
+import { useResourceStore } from "@/stores/resources";
+import { computed, onMounted, reactive } from "vue";
+import {
+  type ResourceOut,
+  type ResourceVersionOut,
+  Status,
+} from "@/client/resource";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import { Tooltip } from "bootstrap";
+import ResourceVersionInfoModal from "@/components/resources/ResourceVersionInfoModal.vue";
+import { useNameStore } from "@/stores/names";
+
+const resourceRepository = useResourceStore();
+const nameRepository = useNameStore();
+let refreshTimeout: NodeJS.Timeout | undefined = undefined;
+
+const resourceState = reactive<{
+  sendingRequest: boolean;
+  loading: boolean;
+  inspectResource?: ResourceOut;
+  inspectVersionIndex: number;
+}>({
+  sendingRequest: false,
+  loading: true,
+  inspectResource: undefined,
+  inspectVersionIndex: 0,
+});
+
+const countItems = computed<number>(() =>
+  resourceRepository.reviewableResources.reduce(
+    (previousValue, currentValue) =>
+      previousValue + currentValue.versions.length,
+    0,
+  ),
+);
+
+function fetchResources() {
+  resourceRepository.fetchReviewableResources(() => {
+    resourceState.loading = false;
+  });
+}
+
+function clickRefreshResources() {
+  clearTimeout(refreshTimeout);
+  refreshTimeout = setTimeout(() => {
+    fetchResources();
+  }, 500);
+}
+
+function syncResource(resourceVersion: ResourceVersionOut) {
+  resourceState.sendingRequest = true;
+  resourceRepository.syncResource(resourceVersion).finally(() => {
+    resourceState.sendingRequest = false;
+  });
+}
+
+onMounted(() => {
+  fetchResources();
+  new Tooltip("#refreshReviewableResourcesButton");
+});
+</script>
+
+<template>
+  <resource-version-info-modal
+    modal-id="review-resource-version-info-modal"
+    :resource-version-index="resourceState.inspectVersionIndex"
+    :resource="resourceState.inspectResource"
+  />
+  <div
+    class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
+  >
+    <h2 class="w-fit">Resource Requests</h2>
+    <span
+      class="w-fit"
+      tabindex="0"
+      data-bs-title="Refresh Reviewable Resources"
+      data-bs-toggle="tooltip"
+      id="refreshReviewableResourcesButton"
+    >
+      <button
+        type="button"
+        class="btn btn-primary btn-light me-2 shadow-sm border w-fit"
+        :disabled="resourceState.loading"
+        @click="clickRefreshResources"
+      >
+        <font-awesome-icon icon="fa-solid fa-arrow-rotate-right" />
+        <span class="visually-hidden">Refresh Reviewable Resources</span>
+      </button>
+    </span>
+  </div>
+  <div v-if="resourceState.loading" class="text-center mt-5">
+    <div class="spinner-border" style="width: 3rem; height: 3rem" role="status">
+      <span class="visually-hidden">Loading...</span>
+    </div>
+  </div>
+  <table
+    class="table caption-top table-striped table-hover align-middle"
+    v-else-if="resourceRepository.reviewableResources.length > 0"
+  >
+    <caption>
+      Display
+      {{
+        countItems
+      }}
+      resource versions
+    </caption>
+    <thead>
+      <tr>
+        <th scope="col">Resource</th>
+        <th scope="col">Release</th>
+        <th scope="col">Status</th>
+        <th scope="col">Maintainer</th>
+        <th scope="col"></th>
+        <th scope="col" class="text-end">Action</th>
+      </tr>
+    </thead>
+    <tbody>
+      <template
+        v-for="resource in resourceRepository.reviewableResources"
+        :key="resource.resource_id"
+      >
+        <tr
+          v-for="(version, index) in resource.versions"
+          :key="version.resource_version_id"
+        >
+          <th>{{ resource.name }}</th>
+          <th>{{ version.release }}</th>
+          <th>{{ version.status }}</th>
+          <th>{{ nameRepository.getName(resource.maintainer_id) }}</th>
+          <th>
+            <button
+              type="button"
+              class="btn btn-secondary"
+              data-bs-toggle="modal"
+              data-bs-target="#review-resource-version-info-modal"
+              @click="
+                resourceState.inspectResource = resource;
+                resourceState.inspectVersionIndex = index;
+              "
+            >
+              Inspect
+            </button>
+          </th>
+          <th class="text-end">
+            <div
+              v-if="version.status === Status.SYNC_REQUESTED"
+              class="btn-group"
+            >
+              <button
+                type="button"
+                class="btn btn-success btn-sm"
+                :disabled="resourceState.sendingRequest"
+                @click="syncResource(version)"
+              >
+                Synchronize
+              </button>
+              <button type="button" class="btn btn-danger btn-sm" disabled>
+                Deny
+              </button>
+            </div>
+            <div
+              v-else-if="version.status === Status.SYNCHRONIZING"
+              class="progress"
+              role="progressbar"
+              aria-label="Animated striped example"
+              aria-valuenow="100"
+              aria-valuemin="0"
+              aria-valuemax="100"
+            >
+              <div
+                class="progress-bar progress-bar-striped progress-bar-animated"
+                style="width: 100%"
+              ></div>
+            </div>
+          </th>
+        </tr>
+      </template>
+    </tbody>
+  </table>
+  <div v-else class="text-center mt-5 fs-4">
+    There are currently no Resources to review
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/src/views/workflows/ArbitraryWorkflowView.vue b/src/views/workflows/ArbitraryWorkflowView.vue
index faedb73a65de83a07412979247856226b7bfd29b..e31a3389eae4d7f2eeb571fb7202f47dc1d59e3b 100644
--- a/src/views/workflows/ArbitraryWorkflowView.vue
+++ b/src/views/workflows/ArbitraryWorkflowView.vue
@@ -10,6 +10,7 @@ import { useWorkflowStore } from "@/stores/workflows";
 import type { WorkflowIn } from "@/client/workflow";
 import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
 import ParameterSchemaFormComponent from "@/components/parameter-schema/ParameterSchemaFormComponent.vue";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const props = defineProps<{
   wid: string;
@@ -148,34 +149,21 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-danger align-items-center border-0"
-      data-bs-autohide="true"
-      id="arbitraryWorkflowExecutionViewErrorToast"
-    >
-      <div class="d-flex p-2 justify-content-between align-items-center">
-        <div class="toast-body">
-          <template v-if="workflowExecutionState.errorType === 'limit'">
-            You have too many active workflow executions to start a new one.
-          </template>
-          <template v-else>
-            There was an error with starting the workflow execution. Look in the
-            console for more information.
-          </template>
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast
+    toast-id="arbitraryWorkflowExecutionViewErrorToast"
+    color-class="danger"
+  >
+    <template> Error starting workflow </template>
+    <template #body>
+      <template v-if="workflowExecutionState.errorType === 'limit'">
+        You have too many active workflow executions to start a new one.
+      </template>
+      <template v-else>
+        There was an error with starting the workflow execution. Look in the
+        console for more information.
+      </template>
+    </template>
+  </bootstrap-toast>
   <template v-if="workflowState.workflow">
     <div class="row m-1 border-bottom mb-4">
       <h1 class="mb-2">Arbitrary Workflow</h1>
diff --git a/src/views/workflows/ListWorkflowExecutionsView.vue b/src/views/workflows/ListWorkflowExecutionsView.vue
index cbf7b1f06fd45f9af5a399a853ea5a78e7f20db7..c3d87aa57e68ecc94186a639712d8e0cd45dd492 100644
--- a/src/views/workflows/ListWorkflowExecutionsView.vue
+++ b/src/views/workflows/ListWorkflowExecutionsView.vue
@@ -9,8 +9,10 @@ import { useWorkflowStore } from "@/stores/workflows";
 import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
 import ParameterModal from "@/components/workflows/modals/ParameterModal.vue";
 import { ExponentialBackoff } from "@/utils/BackoffStrategy";
+import { useNameStore } from "@/stores/names";
 
 const workflowRepository = useWorkflowStore();
+const nameRepository = useNameStore();
 const executionRepository = useWorkflowExecutionStore();
 const backoff = new ExponentialBackoff();
 
@@ -247,8 +249,8 @@ onUnmounted(() => {
                 },
               }"
             >
-              {{ workflowRepository.getName(execution.workflow_id) }}@{{
-                workflowRepository.getName(execution.workflow_version_id)
+              {{ nameRepository.getName(execution.workflow_id) }}@{{
+                nameRepository.getName(execution.workflow_version_id)
               }}
             </router-link>
           </td>
diff --git a/src/views/workflows/ReviewWorkflowsView.vue b/src/views/workflows/ReviewWorkflowsView.vue
index 63ed00942e6f2e04d7c57e76b4a3544de0e07dcd..80baa8d935ffa8908b784ec04f58f0e3aa024681 100644
--- a/src/views/workflows/ReviewWorkflowsView.vue
+++ b/src/views/workflows/ReviewWorkflowsView.vue
@@ -6,8 +6,10 @@ import { determineGitIcon } from "@/utils/GitRepository";
 import { sortedVersions } from "@/utils/Workflow";
 import { useWorkflowStore } from "@/stores/workflows";
 import { useAuthStore } from "@/stores/users";
+import { useNameStore } from "@/stores/names";
 
 const workflowRepository = useWorkflowStore();
+const nameRepository = useNameStore();
 const userRepository = useAuthStore();
 
 const workflowsState = reactive<{
@@ -78,8 +80,8 @@ onMounted(() => {
               {{ workflow.repository_url }}
             </a>
           </td>
-          <td v-if="userRepository.userMapping[workflow.developer_id]">
-            {{ userRepository.userMapping[workflow.developer_id] }}
+          <td v-if="nameRepository.getName(workflow.developer_id)">
+            {{ nameRepository.getName(workflow.developer_id) }}
           </td>
           <td v-else class="placeholder-glow">
             <div class="placeholder w-75"></div>
diff --git a/src/views/workflows/StartWorkflowView.vue b/src/views/workflows/StartWorkflowView.vue
index a8d84cb7da764c53a97ec862daf979d21b76a14a..d215cdaf972649ad24c42ec59b836ee2a495e640 100644
--- a/src/views/workflows/StartWorkflowView.vue
+++ b/src/views/workflows/StartWorkflowView.vue
@@ -10,6 +10,7 @@ import { onMounted, ref, reactive, watch } from "vue";
 import { useRouter } from "vue-router";
 import { Toast } from "bootstrap";
 import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
+import BootstrapToast from "@/components/BootstrapToast.vue";
 
 const executionRepository = useWorkflowExecutionStore();
 const props = defineProps<{
@@ -108,34 +109,21 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="toast-container position-fixed top-toast end-0 p-3">
-    <div
-      role="alert"
-      aria-live="assertive"
-      aria-atomic="true"
-      class="toast text-bg-danger align-items-center border-0"
-      data-bs-autohide="true"
-      id="workflowExecutionViewErrorToast"
-    >
-      <div class="d-flex p-2 justify-content-between align-items-center">
-        <div class="toast-body">
-          <template v-if="versionState.workflowExecutionError === 'limit'">
-            You have too many active workflow executions to start a new one.
-          </template>
-          <template v-else>
-            There was an error with starting the workflow execution. Look in the
-            console for more information.
-          </template>
-        </div>
-        <button
-          type="button"
-          class="btn-close btn-close-white"
-          data-bs-dismiss="toast"
-          aria-label="Close"
-        ></button>
-      </div>
-    </div>
-  </div>
+  <bootstrap-toast
+    toast-id="workflowExecutionViewErrorToast"
+    color-class="danger"
+  >
+    <template>Error starting workflow</template>
+    <template #body>
+      <template v-if="versionState.workflowExecutionError === 'limit'">
+        You have too many active workflow executions to start a new one.
+      </template>
+      <template v-else>
+        There was an error with starting the workflow execution. Look in the
+        console for more information.
+      </template>
+    </template>
+  </bootstrap-toast>
   <parameter-schema-form-component
     :workflow-version-id="versionId"
     :schema="parameterSchema"
diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue
index 2ece0235006271559b4ecc77ba7946f48e5f0a49..08b1395de3189580cd1bc72484199c14f109f38f 100644
--- a/src/views/workflows/WorkflowView.vue
+++ b/src/views/workflows/WorkflowView.vue
@@ -157,7 +157,7 @@ const activeVersionIcon = computed<string | undefined>(
 );
 
 const versionLaunchable = computed<boolean>(
-  () => activeVersion.value?.status == Status.PUBLISHED ?? false,
+  () => activeVersion.value?.status == Status.PUBLISHED,
 );
 
 const gitIcon = computed<string>(() =>
@@ -166,7 +166,7 @@ const gitIcon = computed<string>(() =>
 
 const allowVersionDeprecation = computed<boolean>(() => {
   if (activeVersion.value?.status === Status.PUBLISHED) {
-    if (userRepository.workflowReviewer || userRepository.admin) {
+    if (userRepository.rewiewer || userRepository.admin) {
       return true;
     } else if (
       userRepository.workflowDev &&