Gustavo Zanatta 1 сар өмнө
commit
659af3c3c1
99 өөрчлөгдсөн 21577 нэмэгдсэн , 0 устгасан
  1. 41 0
      README.md
  2. 101 0
      eslint.config.js
  3. 21 0
      index.html
  4. 39 0
      jsconfig.json
  5. 10933 0
      package-lock.json
  6. 55 0
      package.json
  7. 27 0
      postcss.config.js
  8. BIN
      public/favicon.ico
  9. BIN
      public/icons/favicon-128x128.png
  10. BIN
      public/icons/favicon-16x16.png
  11. BIN
      public/icons/favicon-32x32.png
  12. BIN
      public/icons/favicon-96x96.png
  13. 236 0
      quasar.config.js
  14. 43 0
      src/App.vue
  15. 183 0
      src/api/cacheService.js
  16. 38 0
      src/api/city.js
  17. 28 0
      src/api/country.js
  18. 2 0
      src/api/index.js
  19. 11 0
      src/api/permission.js
  20. 33 0
      src/api/state.js
  21. 31 0
      src/api/user.js
  22. BIN
      src/assets/logo.png
  23. 0 0
      src/assets/softpar_logo_dark.svg
  24. 0 0
      src/assets/softpar_logo_light.svg
  25. 1 0
      src/assets/softpar_logo_mini.svg
  26. 0 0
      src/boot/.gitkeep
  27. 126 0
      src/boot/axios.js
  28. 42 0
      src/boot/defaultPropsComponents.js
  29. 18 0
      src/boot/i18n.js
  30. 158 0
      src/boot/socket.io.js
  31. 77 0
      src/components/charts/CardIconChart.vue
  32. 81 0
      src/components/charts/CardIconMiniChart.vue
  33. 285 0
      src/components/charts/custom/NPSChart.vue
  34. 201 0
      src/components/charts/custom/SpeedometerChart.vue
  35. 41 0
      src/components/charts/maps/Brasil/DadosBrasil.js
  36. 228 0
      src/components/charts/maps/Brasil/MapaBrasil.vue
  37. 110 0
      src/components/charts/maps/MapaEstado.vue
  38. 14 0
      src/components/charts/maps/Paraguai/DadosParaguai.js
  39. 225 0
      src/components/charts/maps/Paraguai/MapaParaguai.vue
  40. 101 0
      src/components/charts/mini/MiniBarChart.vue
  41. 116 0
      src/components/charts/mini/MiniLineChart.vue
  42. 267 0
      src/components/charts/normal/BarChart.vue
  43. 204 0
      src/components/charts/normal/DoughnutChart.vue
  44. 265 0
      src/components/charts/normal/LineChart.vue
  45. 205 0
      src/components/charts/normal/PieChart.vue
  46. 103 0
      src/components/defaults/DefaultCepInput.vue
  47. 80 0
      src/components/defaults/DefaultCurrencyInput.vue
  48. 58 0
      src/components/defaults/DefaultDialogHeader.vue
  49. 257 0
      src/components/defaults/DefaultFilePicker.vue
  50. 162 0
      src/components/defaults/DefaultInputDatePicker.vue
  51. 42 0
      src/components/defaults/DefaultPasswordInput.vue
  52. 326 0
      src/components/defaults/DefaultTable.vue
  53. 373 0
      src/components/defaults/DefaultTableServerSide.vue
  54. 32 0
      src/components/defaults/DefaultTabs.vue
  55. 19 0
      src/components/layout/DefaultHeaderPage.vue
  56. 340 0
      src/components/layout/LeftMenuLayout.vue
  57. 128 0
      src/components/layout/LeftMenuLayoutMobile.vue
  58. 168 0
      src/components/regions/CitySelect.vue
  59. 117 0
      src/components/regions/CountrySelect.vue
  60. 167 0
      src/components/regions/StateSelect.vue
  61. 57 0
      src/composables/useAuth.js
  62. 173 0
      src/composables/useFormUpdateTracker.js
  63. 111 0
      src/composables/useInputRules.js
  64. 81 0
      src/composables/useScroll.js
  65. 94 0
      src/composables/useSubmitHandler.js
  66. 92 0
      src/css/app.scss
  67. 167 0
      src/css/quasar.variables.scss
  68. 198 0
      src/css/table.scss
  69. 59 0
      src/helpers/convertBase64Image.js
  70. 29 0
      src/helpers/masks.js
  71. 112 0
      src/helpers/utils.js
  72. 33 0
      src/i18n/index.js
  73. 335 0
      src/i18n/locales/en.json
  74. 335 0
      src/i18n/locales/es.json
  75. 335 0
      src/i18n/locales/pt.json
  76. 7 0
      src/layouts/LoginLayout.vue
  77. 145 0
      src/layouts/MainLayout.vue
  78. 30 0
      src/pages/ErrorNotFound.vue
  79. 136 0
      src/pages/LoginPage.vue
  80. 130 0
      src/pages/city/CityPage.vue
  81. 158 0
      src/pages/city/components/AddEditCityDialog.vue
  82. 125 0
      src/pages/country/CountryPage.vue
  83. 130 0
      src/pages/country/components/AddEditCountryDialog.vue
  84. 381 0
      src/pages/dashboard/DashboardPage.vue
  85. 47 0
      src/pages/dashboard/components/DatePeriodSelector.vue
  86. 125 0
      src/pages/state/StatePage.vue
  87. 147 0
      src/pages/state/components/AddEditStateDialog.vue
  88. 108 0
      src/pages/users/UsersPage.vue
  89. 140 0
      src/pages/users/components/AddEditUserDialog.vue
  90. 108 0
      src/pages/users/components/UserTypeSelect.vue
  91. 69 0
      src/router/index.js
  92. 57 0
      src/router/routes.js
  93. 62 0
      src/router/routes/regions.route.js
  94. 22 0
      src/router/routes/users.route.js
  95. 20 0
      src/stores/index.js
  96. 90 0
      src/stores/navigation.js
  97. 126 0
      src/stores/permission.js
  98. 10 0
      src/stores/store-flag.d.ts
  99. 34 0
      src/stores/user.js

+ 41 - 0
README.md

@@ -0,0 +1,41 @@
+# Quasar App (quasar-skeleton)
+
+A skeleton for future projects
+
+## Install the dependencies
+```bash
+yarn
+# or
+npm install
+```
+
+### Start the app in development mode (hot-code reloading, error reporting, etc.)
+```bash
+quasar dev
+```
+
+
+### Lint the files
+```bash
+yarn lint
+# or
+npm run lint
+```
+
+
+### Format the files
+```bash
+yarn format
+# or
+npm run format
+```
+
+
+
+### Build the app for production
+```bash
+quasar build
+```
+
+### Customize the configuration
+See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js).

+ 101 - 0
eslint.config.js

@@ -0,0 +1,101 @@
+import js from "@eslint/js";
+import globals from "globals";
+import pluginVue from "eslint-plugin-vue";
+import pluginQuasar from "@quasar/app-vite/eslint";
+import vueI18n from "@intlify/eslint-plugin-vue-i18n";
+
+// the following is optional, if you want prettier too:
+import prettierSkipFormatting from "@vue/eslint-config-prettier/skip-formatting";
+
+export default [
+  {
+    /**
+     * Ignore the following files.
+     * Please note that pluginQuasar.configs.recommended() already ignores
+     * the "node_modules" folder for you (and all other Quasar project
+     * relevant folders and files).
+     *
+     * ESLint requires "ignores" key to be the only one in this object
+     */
+    ignores: [
+      "/dist",
+      "/src-capacitor",
+      "/src-cordova",
+      "/.quasar",
+      "/node_modules",
+      "/quasar.config.*.temporary.compiled*",
+    ],
+  },
+  ...pluginQuasar.configs.recommended(),
+  js.configs.recommended,
+
+  /**
+   * https://eslint.vuejs.org
+   *
+   * pluginVue.configs.base
+   *   -> Settings and rules to enable correct ESLint parsing.
+   * pluginVue.configs[ 'flat/essential']
+   *   -> base, plus rules to prevent errors or unintended behavior.
+   * pluginVue.configs["flat/strongly-recommended"]
+   *   -> Above, plus rules to considerably improve code readability and/or dev experience.
+   * pluginVue.configs["flat/recommended"]
+   *   -> Above, plus rules to enforce subjective community defaults to ensure consistency.
+   */
+  ...pluginVue.configs["flat/essential"],
+  ...pluginVue.configs["flat/strongly-recommended"],
+  ...pluginVue.configs["flat/recommended"],
+  ...vueI18n.configs.recommended,
+
+  {
+    languageOptions: {
+      ecmaVersion: "latest",
+      sourceType: "module",
+
+      globals: {
+        ...globals.browser,
+        ...globals.node, // SSR, Electron, config files
+        process: "readonly", // process.env.*
+        ga: "readonly", // Google Analytics
+        cordova: "readonly",
+        Capacitor: "readonly",
+        chrome: "readonly", // BEX related
+        browser: "readonly", // BEX related
+      },
+    },
+
+    // add your custom rules here
+    rules: {
+      "vue/no-unused-vars": "warn",
+      "vue/no-unused-components": "warn",
+      "@intlify/vue-i18n/no-dynamic-keys": "off",
+      "@intlify/vue-i18n/no-unused-keys": [
+        "error",
+        {
+          extensions: [".js", ".vue"],
+        },
+      ],
+      // allow debugger during development only
+      "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
+    },
+  },
+
+  {
+    settings: {
+      "vue-i18n": {
+        localeDir: "./src/i18n/locales/*.{json,json5,yaml,yml}",
+        messageSyntaxVersion: "^11.0.0",
+      },
+    },
+  },
+
+  {
+    files: ["src-pwa/custom-service-worker.js"],
+    languageOptions: {
+      globals: {
+        ...globals.serviceworker,
+      },
+    },
+  },
+
+  prettierSkipFormatting, // optional, if you want prettier
+];

+ 21 - 0
index.html

@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title><%= productName %></title>
+
+    <meta charset="utf-8">
+    <meta name="description" content="<%= productDescription %>">
+    <meta name="format-detection" content="telephone=no">
+    <meta name="msapplication-tap-highlight" content="no">
+    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
+
+    <link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
+    <link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
+    <link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
+    <link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
+    <link rel="icon" type="image/ico" href="favicon.ico">
+  </head>
+  <body>
+    <!-- quasar:entry-point -->
+  </body>
+</html>

+ 39 - 0
jsconfig.json

@@ -0,0 +1,39 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "src/*": [
+        "src/*"
+      ],
+      "app/*": [
+        "*"
+      ],
+      "components/*": [
+        "src/components/*"
+      ],
+      "layouts/*": [
+        "src/layouts/*"
+      ],
+      "pages/*": [
+        "src/pages/*"
+      ],
+      "assets/*": [
+        "src/assets/*"
+      ],
+      "boot/*": [
+        "src/boot/*"
+      ],
+      "stores/*": [
+        "src/stores/*"
+      ],
+      "vue$": [
+        "node_modules/vue/dist/vue.runtime.esm-bundler.js"
+      ]
+    }
+  },
+  "exclude": [
+    "dist",
+    ".quasar",
+    "node_modules"
+  ]
+}

+ 10933 - 0
package-lock.json

@@ -0,0 +1,10933 @@
+{
+  "name": "quasar-skeleton",
+  "version": "0.0.1",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "quasar-skeleton",
+      "version": "0.0.1",
+      "dependencies": {
+        "@bufbuild/protobuf": "^2.5.1",
+        "@quasar/cli": "^2.5.0",
+        "@quasar/extras": "^1.17.0",
+        "axios": "^1.13.2",
+        "chart.js": "^4.5.1",
+        "chartjs-plugin-datalabels": "^2.2.0",
+        "date-fns": "^3.6.0",
+        "fast-deep-equal": "^3.1.3",
+        "pinia": "^3.0.2",
+        "quasar": "^2.18.6",
+        "socket.io-client": "^4.8.1",
+        "vue": "^3.5.13",
+        "vue-chartjs": "^5.3.3",
+        "vue-currency-input": "^3.2.1",
+        "vue-i18n": "^11.1.4",
+        "vue-router": "^4.6.4"
+      },
+      "devDependencies": {
+        "@bufbuild/buf": "^1.61.0",
+        "@bufbuild/protoc-gen-es": "^2.10.2",
+        "@eslint/js": "^9.27.0",
+        "@intlify/eslint-plugin-vue-i18n": "^4.0.1",
+        "@intlify/unplugin-vue-i18n": "^6.0.8",
+        "@quasar/app-vite": "^2.4.0",
+        "@vue/eslint-config-prettier": "^10.2.0",
+        "autoprefixer": "^10.4.21",
+        "eslint": "^9.31.0",
+        "eslint-config-prettier": "^10.1.5",
+        "eslint-plugin-vue": "^10.1.0",
+        "postcss": "^8.5.3",
+        "prettier": "^3.5.3",
+        "vite-plugin-checker": "^0.9.3"
+      },
+      "engines": {
+        "node": "^24 || ^22 || ^20 || ^18",
+        "npm": ">= 6.13.4",
+        "yarn": ">= 1.21.1"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+      "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.27.1",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+      "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.28.5"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.28.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+      "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+      "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.28.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@bufbuild/buf": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.61.0.tgz",
+      "integrity": "sha512-9kKZK/GjhIDUj50pngmjtC6bGFl6U1rFDH2gZJATRfp2vNnQPsb6BqZ+2lea37bgHBYdAYttlve8wnXszXmftA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "buf": "bin/buf",
+        "protoc-gen-buf-breaking": "bin/protoc-gen-buf-breaking",
+        "protoc-gen-buf-lint": "bin/protoc-gen-buf-lint"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@bufbuild/buf-darwin-arm64": "1.61.0",
+        "@bufbuild/buf-darwin-x64": "1.61.0",
+        "@bufbuild/buf-linux-aarch64": "1.61.0",
+        "@bufbuild/buf-linux-armv7": "1.61.0",
+        "@bufbuild/buf-linux-x64": "1.61.0",
+        "@bufbuild/buf-win32-arm64": "1.61.0",
+        "@bufbuild/buf-win32-x64": "1.61.0"
+      }
+    },
+    "node_modules/@bufbuild/buf-darwin-arm64": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.61.0.tgz",
+      "integrity": "sha512-8vUGNV65LNPp+HT3NuCT9i/mCUEyLrSFctJ2Dz8JqnUu8fVPm4f8lVBSCT0TYLoQ8o8xb/A7bwWu14aKfXxgCg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@bufbuild/buf-darwin-x64": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.61.0.tgz",
+      "integrity": "sha512-dyJghwTYXT6e/Ec+2iPWijquTzyT+5vTItX4R7hM/soNQOs4eEo2z7EHLqvclHyi7+p7+8xye3z+BFM33ucM8A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@bufbuild/buf-linux-aarch64": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.61.0.tgz",
+      "integrity": "sha512-9r5DRqwpq3WX0ltzt1p/Oe+8g679Fg4XJLaH/zmjqpwHVO+tNzYy7PHK4qWg83PCoANw6OVizl+iTmnE0vbklg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@bufbuild/buf-linux-armv7": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-armv7/-/buf-linux-armv7-1.61.0.tgz",
+      "integrity": "sha512-P2cSlzu68omZ6kyijMcYezZJFS4XayfLA0ATm1HP0zUaV+xVoeMOGhKvMWUFAa9SGYUs4Drxi2fyK/c2ZFA8sw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@bufbuild/buf-linux-x64": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.61.0.tgz",
+      "integrity": "sha512-USDSLB0vkrR6Sk/VDtECLdOKHfynenCjnAchr9bdqC137IVJT7TVT8JhBW0UWMmXDZYjwbDrBLITwWj8hwk6Uw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@bufbuild/buf-win32-arm64": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.61.0.tgz",
+      "integrity": "sha512-36n90c0lzhDMXRKhht8XreUCha4OTY7yR6g+bnkAFUXSbCbR3BpqrenWvl5NBtfX2Y70dmvjKnGwVanSIrb1uA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@bufbuild/buf-win32-x64": {
+      "version": "1.61.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.61.0.tgz",
+      "integrity": "sha512-uBIU7tQlCBPyoeJZH0NzL6y9Y4ikje4OlgHYGssbaPLsCKELSFDHQkHFabkwnVeXSUf9Intq+U3jtb+qOIlflw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@bufbuild/protobuf": {
+      "version": "2.10.2",
+      "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.10.2.tgz",
+      "integrity": "sha512-uFsRXwIGyu+r6AMdz+XijIIZJYpoWeYzILt5yZ2d3mCjQrWUTVpVD9WL/jZAbvp+Ed04rOhrsk7FiTcEDseB5A==",
+      "license": "(Apache-2.0 AND BSD-3-Clause)"
+    },
+    "node_modules/@bufbuild/protoc-gen-es": {
+      "version": "2.10.2",
+      "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-2.10.2.tgz",
+      "integrity": "sha512-vbjPsuofbtZwZXuOP7Y16CQsxrwCjuRONffmJSBEhoC7PQu/Cabp0+Fu/poLPm9CNM0tDCQA0xvgobgudaEYxQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@bufbuild/protobuf": "2.10.2",
+        "@bufbuild/protoplugin": "2.10.2"
+      },
+      "bin": {
+        "protoc-gen-es": "bin/protoc-gen-es"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "peerDependencies": {
+        "@bufbuild/protobuf": "2.10.2"
+      },
+      "peerDependenciesMeta": {
+        "@bufbuild/protobuf": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@bufbuild/protoplugin": {
+      "version": "2.10.2",
+      "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-2.10.2.tgz",
+      "integrity": "sha512-RAWVs9tCzRqSS3tUtaFhOcauOAazCrm7tlGh0WHFq/44n5Fj6YgefdlZEPIaAK6VAA+FdOoFgtOJK2Ji5U24pw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@bufbuild/protobuf": "2.10.2",
+        "@typescript/vfs": "^1.6.2",
+        "typescript": "5.4.5"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+      "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+      "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+      "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+      "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+      "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+      "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+      "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+      "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+      "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+      "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+      "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+      "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+      "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+      "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+      "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+      "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+      "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+      "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+      "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+      "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+      "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+      "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.4.3"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.12.2",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+      "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/config-array": {
+      "version": "0.21.1",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+      "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/object-schema": "^2.1.7",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.2"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/config-helpers": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^0.17.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/core": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/json-schema": "^7.0.15"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+      "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.1",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/ignore": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "9.39.2",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+      "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/plugin-kit": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^0.17.0",
+        "levn": "^0.4.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+      "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node": {
+      "version": "0.16.7",
+      "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+      "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanfs/core": "^0.19.1",
+        "@humanwhocodes/retry": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/retry": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+      "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@inquirer/external-editor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
+      "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chardet": "^2.1.1",
+        "iconv-lite": "^0.7.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/external-editor/node_modules/iconv-lite": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz",
+      "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/@inquirer/figures": {
+      "version": "1.0.15",
+      "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz",
+      "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@intlify/bundle-utils": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-10.0.1.tgz",
+      "integrity": "sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/message-compiler": "^11.1.2",
+        "@intlify/shared": "^11.1.2",
+        "acorn": "^8.8.2",
+        "escodegen": "^2.1.0",
+        "estree-walker": "^2.0.2",
+        "jsonc-eslint-parser": "^2.3.0",
+        "mlly": "^1.2.0",
+        "source-map-js": "^1.0.1",
+        "yaml-eslint-parser": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependenciesMeta": {
+        "petite-vue-i18n": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/core-base": {
+      "version": "11.2.2",
+      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.2.2.tgz",
+      "integrity": "sha512-0mCTBOLKIqFUP3BzwuFW23hYEl9g/wby6uY//AC5hTgQfTsM2srCYF2/hYGp+a5DZ/HIFIgKkLJMzXTt30r0JQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/message-compiler": "11.2.2",
+        "@intlify/shared": "11.2.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/eslint-plugin-vue-i18n": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@intlify/eslint-plugin-vue-i18n/-/eslint-plugin-vue-i18n-4.1.0.tgz",
+      "integrity": "sha512-MPAr3LGTrkB5CZBHN5eUf4kASUEiSaDM371jADmxNbTL1Ew7IAyCIBGm3+/1sWcvsfVHe4wz8RFoo6FpeQZ4Nw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint/eslintrc": "^3.0.0",
+        "@intlify/core-base": "^11.0.0",
+        "@intlify/message-compiler": "^11.0.0",
+        "debug": "^4.3.4",
+        "eslint-compat-utils": "^0.6.0",
+        "glob": "^10.3.3",
+        "globals": "^16.0.0",
+        "ignore": "^7.0.0",
+        "import-fresh": "^3.3.0",
+        "is-language-code": "^3.1.0",
+        "js-yaml": "^4.1.0",
+        "json5": "^2.2.3",
+        "lodash": "^4.17.21",
+        "parse5": "^7.1.2",
+        "semver": "^7.5.4",
+        "synckit": "^0.10.0"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^8.0.0 || ^9.0.0-0",
+        "jsonc-eslint-parser": "^2.3.0",
+        "vue-eslint-parser": "^10.0.0",
+        "yaml-eslint-parser": "^1.2.2"
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "11.2.2",
+      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.2.2.tgz",
+      "integrity": "sha512-XS2p8Ff5JxWsKhgfld4/MRQzZRQ85drMMPhb7Co6Be4ZOgqJX1DzcZt0IFgGTycgqL8rkYNwgnD443Q+TapOoA==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/shared": "11.2.2",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "11.2.2",
+      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.2.2.tgz",
+      "integrity": "sha512-OtCmyFpSXxNu/oET/aN6HtPCbZ01btXVd0f3w00YsHOb13Kverk1jzA2k47pAekM55qbUw421fvPF1yxZ+gicw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/unplugin-vue-i18n": {
+      "version": "6.0.8",
+      "resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-6.0.8.tgz",
+      "integrity": "sha512-Vvm3KhjE6TIBVUQAk37rBiaYy2M5OcWH0ZcI1XKEsOTeN1o0bErk+zeuXmcrcMc/73YggfI8RoxOUz9EB/69JQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "@intlify/bundle-utils": "^10.0.1",
+        "@intlify/shared": "^11.1.2",
+        "@intlify/vue-i18n-extensions": "^8.0.0",
+        "@rollup/pluginutils": "^5.1.0",
+        "@typescript-eslint/scope-manager": "^8.13.0",
+        "@typescript-eslint/typescript-estree": "^8.13.0",
+        "debug": "^4.3.3",
+        "fast-glob": "^3.2.12",
+        "js-yaml": "^4.1.0",
+        "json5": "^2.2.3",
+        "pathe": "^1.0.0",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2",
+        "unplugin": "^1.1.0",
+        "vue": "^3.4"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "petite-vue-i18n": "*",
+        "vue": "^3.2.25",
+        "vue-i18n": "*"
+      },
+      "peerDependenciesMeta": {
+        "petite-vue-i18n": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-8.0.0.tgz",
+      "integrity": "sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.24.6",
+        "@intlify/shared": "^10.0.0",
+        "@vue/compiler-dom": "^3.2.45",
+        "vue-i18n": "^10.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@intlify/shared": "^9.0.0 || ^10.0.0 || ^11.0.0",
+        "@vue/compiler-dom": "^3.0.0",
+        "vue": "^3.0.0",
+        "vue-i18n": "^9.0.0 || ^10.0.0 || ^11.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@intlify/shared": {
+          "optional": true
+        },
+        "@vue/compiler-dom": {
+          "optional": true
+        },
+        "vue": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/core-base": {
+      "version": "10.0.8",
+      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.8.tgz",
+      "integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/message-compiler": "10.0.8",
+        "@intlify/shared": "10.0.8"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/message-compiler": {
+      "version": "10.0.8",
+      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.8.tgz",
+      "integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/shared": "10.0.8",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/shared": {
+      "version": "10.0.8",
+      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz",
+      "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@intlify/vue-i18n-extensions/node_modules/vue-i18n": {
+      "version": "10.0.8",
+      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.8.tgz",
+      "integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/core-base": "10.0.8",
+        "@intlify/shared": "10.0.8",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/@isaacs/cliui": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^5.1.2",
+        "string-width-cjs": "npm:string-width@^4.2.0",
+        "strip-ansi": "^7.0.1",
+        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+        "wrap-ansi": "^8.1.0",
+        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@isaacs/cliui/node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.1.0",
+        "string-width": "^5.0.1",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.11",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+      "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@kurkle/color": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
+      "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
+      "license": "MIT"
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@parcel/watcher": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
+      "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "detect-libc": "^1.0.3",
+        "is-glob": "^4.0.3",
+        "micromatch": "^4.0.5",
+        "node-addon-api": "^7.0.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher-android-arm64": "2.5.1",
+        "@parcel/watcher-darwin-arm64": "2.5.1",
+        "@parcel/watcher-darwin-x64": "2.5.1",
+        "@parcel/watcher-freebsd-x64": "2.5.1",
+        "@parcel/watcher-linux-arm-glibc": "2.5.1",
+        "@parcel/watcher-linux-arm-musl": "2.5.1",
+        "@parcel/watcher-linux-arm64-glibc": "2.5.1",
+        "@parcel/watcher-linux-arm64-musl": "2.5.1",
+        "@parcel/watcher-linux-x64-glibc": "2.5.1",
+        "@parcel/watcher-linux-x64-musl": "2.5.1",
+        "@parcel/watcher-win32-arm64": "2.5.1",
+        "@parcel/watcher-win32-ia32": "2.5.1",
+        "@parcel/watcher-win32-x64": "2.5.1"
+      }
+    },
+    "node_modules/@parcel/watcher-android-arm64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
+      "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-arm64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
+      "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-x64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
+      "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-freebsd-x64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
+      "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-glibc": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
+      "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-musl": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
+      "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-glibc": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
+      "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-musl": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
+      "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-glibc": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
+      "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-musl": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
+      "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-arm64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
+      "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-ia32": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
+      "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-x64": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
+      "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@pkgr/core": {
+      "version": "0.2.9",
+      "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
+      "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/pkgr"
+      }
+    },
+    "node_modules/@pnpm/config.env-replace": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
+      "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.22.0"
+      }
+    },
+    "node_modules/@pnpm/network.ca-file": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
+      "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "4.2.10"
+      },
+      "engines": {
+        "node": ">=12.22.0"
+      }
+    },
+    "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": {
+      "version": "4.2.10",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+      "license": "ISC"
+    },
+    "node_modules/@pnpm/npm-conf": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz",
+      "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==",
+      "license": "MIT",
+      "dependencies": {
+        "@pnpm/config.env-replace": "^1.1.0",
+        "@pnpm/network.ca-file": "^1.0.1",
+        "config-chain": "^1.1.11"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@quasar/app-vite": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-2.4.0.tgz",
+      "integrity": "sha512-nfdcfERQ1bdUFsgXfYexgUAGBrsRHuzlik5p58cKGpYXiwUZZN6mJhN8VxU/zGT0GYHHiNIZlb67N+R52NYd6Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@quasar/render-ssr-error": "^1.0.3",
+        "@quasar/ssl-certificate": "^1.0.0",
+        "@quasar/vite-plugin": "^1.10.0",
+        "@types/chrome": "^0.0.262",
+        "@types/compression": "^1.7.5",
+        "@types/cordova": "^11.0.3",
+        "@types/express": "^4.17.21",
+        "@vitejs/plugin-vue": "^6.0.1",
+        "archiver": "^7.0.1",
+        "chokidar": "^3.6.0",
+        "ci-info": "^4.0.0",
+        "compression": "^1.7.5",
+        "confbox": "^0.1.8",
+        "cross-spawn": "^7.0.6",
+        "dot-prop": "9.0.0",
+        "dotenv": "^16.4.5",
+        "dotenv-expand": "^11.0.6",
+        "elementtree": "0.1.7",
+        "esbuild": "^0.25.0",
+        "express": "^4.21.2",
+        "fs-extra": "^11.2.0",
+        "html-minifier-terser": "^7.2.0",
+        "inquirer": "^9.3.7",
+        "isbinaryfile": "^5.0.4",
+        "kolorist": "^1.8.0",
+        "lodash": "^4.17.21",
+        "minimist": "^1.2.8",
+        "mlly": "^1.7.4",
+        "open": "^10.1.0",
+        "rollup-plugin-visualizer": "^5.13.1",
+        "sass-embedded": "^1.83.0",
+        "semver": "^7.6.3",
+        "serialize-javascript": "^6.0.2",
+        "tinyglobby": "^0.2.10",
+        "ts-essentials": "^9.4.2",
+        "vite": "^7.0.3",
+        "webpack-merge": "^6.0.1"
+      },
+      "bin": {
+        "quasar": "bin/quasar.js"
+      },
+      "engines": {
+        "node": "^30 || ^28 || ^26 || ^24 || ^22 || ^20",
+        "npm": ">= 6.14.12",
+        "yarn": ">= 1.17.3"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://donate.quasar.dev"
+      },
+      "peerDependencies": {
+        "@electron/packager": ">= 18",
+        "electron-builder": ">= 22",
+        "pinia": "^2.0.0 || ^3.0.0",
+        "quasar": "^2.16.0",
+        "typescript": ">= 5.4",
+        "vue": "^3.2.29",
+        "vue-router": "^4.0.12",
+        "workbox-build": ">= 6"
+      },
+      "peerDependenciesMeta": {
+        "@electron/packager": {
+          "optional": true
+        },
+        "electron-builder": {
+          "optional": true
+        },
+        "eslint": {
+          "optional": true
+        },
+        "pinia": {
+          "optional": true
+        },
+        "typescript": {
+          "optional": true
+        },
+        "workbox-build": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@quasar/cli": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/@quasar/cli/-/cli-2.5.0.tgz",
+      "integrity": "sha512-2Vdltr47k7iwjSAYdtpu2ekPdGCmtrKU84wrGMs4taPRsfFyVyRBnxM1jruSvcmk54eA5chwof+ljmrui37AOA==",
+      "license": "MIT",
+      "dependencies": {
+        "@quasar/ssl-certificate": "^1.0.0",
+        "ci-info": "^4.0.0",
+        "compression": "^1.7.4",
+        "connect-history-api-fallback": "^2.0.0",
+        "cors": "^2.8.5",
+        "cross-spawn": "^7.0.6",
+        "express": "^4.21.2",
+        "fs-extra": "^11.2.0",
+        "http-proxy-middleware": "^2.0.7",
+        "kolorist": "^1.8.0",
+        "minimist": "^1.2.8",
+        "open": "^9.1.0",
+        "route-cache": "^0.5.0",
+        "update-notifier": "^6.0.2"
+      },
+      "bin": {
+        "quasar": "bin/quasar.js"
+      },
+      "engines": {
+        "node": ">= 16",
+        "npm": ">= 5.6.0",
+        "yarn": ">= 1.6.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://donate.quasar.dev"
+      }
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "MIT",
+      "dependencies": {
+        "run-applescript": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "MIT",
+      "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/@quasar/cli/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==",
+      "license": "MIT",
+      "dependencies": {
+        "bplist-parser": "^0.2.0",
+        "untildify": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.17.0"
+      }
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "MIT",
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "MIT",
+      "dependencies": {
+        "is-docker": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@quasar/cli/node_modules/open": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
+      "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
+      "license": "MIT",
+      "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/@quasar/cli/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==",
+      "license": "MIT",
+      "dependencies": {
+        "execa": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "MIT",
+      "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"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "ISC"
+    },
+    "node_modules/@quasar/cli/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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@quasar/extras": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.17.0.tgz",
+      "integrity": "sha512-KqAHdSJfIDauiR1nJ8rqHWT0diqD0QradZKoVIZJAilHAvgwyPIY7MbyR2z4RIMkUIMUSqBZcbshMpEw+9A30w==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://donate.quasar.dev"
+      }
+    },
+    "node_modules/@quasar/render-ssr-error": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@quasar/render-ssr-error/-/render-ssr-error-1.0.3.tgz",
+      "integrity": "sha512-A8RF99q6/sOSe1Ighnh5syEIbliD3qUYEJd2HyfFyBPSMF+WYGXon5dmzg4nUoK662NgOggInevkDyBDJcZugg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "stack-trace": "^1.0.0-pre2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://donate.quasar.dev"
+      }
+    },
+    "node_modules/@quasar/ssl-certificate": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@quasar/ssl-certificate/-/ssl-certificate-1.0.0.tgz",
+      "integrity": "sha512-RhZF7rO76T7Ywer1/5lCe7xl3CIiXxSAH1xgwOj0wcHTityDxJqIN/5YIj6BxMvlFw8XkJDoB1udEQafoVFA4g==",
+      "license": "MIT",
+      "dependencies": {
+        "fs-extra": "^11.1.1",
+        "selfsigned": "^2.1.1"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://donate.quasar.dev"
+      }
+    },
+    "node_modules/@quasar/vite-plugin": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/@quasar/vite-plugin/-/vite-plugin-1.10.0.tgz",
+      "integrity": "sha512-4PJoTclz4ZjAfyqe0+hlkKcFJt0e2NX3Ac3hy8ILqUPdtZ24nCo5/xEHvTxZGBQMKRPwwePbO8CVs4n9EKJEug==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://donate.quasar.dev"
+      },
+      "peerDependencies": {
+        "@vitejs/plugin-vue": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
+        "quasar": "^2.16.0",
+        "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/@rolldown/pluginutils": {
+      "version": "1.0.0-beta.53",
+      "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
+      "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@rollup/pluginutils": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
+      "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0",
+        "estree-walker": "^2.0.2",
+        "picomatch": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+      },
+      "peerDependenciesMeta": {
+        "rollup": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.5.tgz",
+      "integrity": "sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.5.tgz",
+      "integrity": "sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.5.tgz",
+      "integrity": "sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.5.tgz",
+      "integrity": "sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.5.tgz",
+      "integrity": "sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.5.tgz",
+      "integrity": "sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.5.tgz",
+      "integrity": "sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.5.tgz",
+      "integrity": "sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.5.tgz",
+      "integrity": "sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.5.tgz",
+      "integrity": "sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-gnu": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.5.tgz",
+      "integrity": "sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.5.tgz",
+      "integrity": "sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.5.tgz",
+      "integrity": "sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.5.tgz",
+      "integrity": "sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.5.tgz",
+      "integrity": "sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.5.tgz",
+      "integrity": "sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.5.tgz",
+      "integrity": "sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-openharmony-arm64": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.5.tgz",
+      "integrity": "sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.5.tgz",
+      "integrity": "sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.5.tgz",
+      "integrity": "sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-gnu": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.5.tgz",
+      "integrity": "sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.5.tgz",
+      "integrity": "sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@sindresorhus/is": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz",
+      "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/is?sponsor=1"
+      }
+    },
+    "node_modules/@socket.io/component-emitter": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+      "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+      "license": "MIT"
+    },
+    "node_modules/@szmarczak/http-timer": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+      "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+      "license": "MIT",
+      "dependencies": {
+        "defer-to-connect": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=14.16"
+      }
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.6",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/chrome": {
+      "version": "0.0.262",
+      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.262.tgz",
+      "integrity": "sha512-TOoj3dqSYE13PD2fRuMQ6X6pggEvL9rRk/yOYOyWE6sfqRWxsJm4VoVm+wr9pkr4Sht/M5t7FFL4vXato8d1gA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/filesystem": "*",
+        "@types/har-format": "*"
+      }
+    },
+    "node_modules/@types/compression": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz",
+      "integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/express": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.38",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/cordova": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-11.0.3.tgz",
+      "integrity": "sha512-kyuRQ40/NWQVhqGIHq78Ehu2Bf9Mlg0LhmSmis6ZFJK7z933FRfYi8tHe/k/0fB+PGfCf95rJC6TO7dopaFvAg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/express": {
+      "version": "4.17.25",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
+      "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^4.17.33",
+        "@types/qs": "*",
+        "@types/serve-static": "^1"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "4.19.7",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz",
+      "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/filesystem": {
+      "version": "0.0.36",
+      "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz",
+      "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/filewriter": "*"
+      }
+    },
+    "node_modules/@types/filewriter": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz",
+      "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/har-format": {
+      "version": "1.2.16",
+      "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz",
+      "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/http-cache-semantics": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+      "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/http-proxy": {
+      "version": "1.17.17",
+      "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz",
+      "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "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,
+      "license": "MIT"
+    },
+    "node_modules/@types/mime": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
+      "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/node": {
+      "version": "25.0.2",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.2.tgz",
+      "integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~7.16.0"
+      }
+    },
+    "node_modules/@types/node-forge": {
+      "version": "1.3.14",
+      "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz",
+      "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/send": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+      "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "1.15.10",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
+      "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*",
+        "@types/send": "<1"
+      }
+    },
+    "node_modules/@types/serve-static/node_modules/@types/send": {
+      "version": "0.17.6",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
+      "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/mime": "^1",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@typescript-eslint/project-service": {
+      "version": "8.50.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz",
+      "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/tsconfig-utils": "^8.50.0",
+        "@typescript-eslint/types": "^8.50.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "8.50.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.0.tgz",
+      "integrity": "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.50.0",
+        "@typescript-eslint/visitor-keys": "8.50.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/tsconfig-utils": {
+      "version": "8.50.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz",
+      "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "8.50.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz",
+      "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "8.50.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz",
+      "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/project-service": "8.50.0",
+        "@typescript-eslint/tsconfig-utils": "8.50.0",
+        "@typescript-eslint/types": "8.50.0",
+        "@typescript-eslint/visitor-keys": "8.50.0",
+        "debug": "^4.3.4",
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "tinyglobby": "^0.2.15",
+        "ts-api-utils": "^2.1.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "8.50.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz",
+      "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.50.0",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@typescript/vfs": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.2.tgz",
+      "integrity": "sha512-hoBwJwcbKHmvd2QVebiytN1aELvpk9B74B4L1mFm/XT1Q/VOYAWl2vQ9AWRFtQq8zmz6enTpfTV8WRc4ATjW/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.1.1"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      }
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz",
+      "integrity": "sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@rolldown/pluginutils": "1.0.0-beta.53"
+      },
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      },
+      "peerDependencies": {
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
+      "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/shared": "3.5.25",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz",
+      "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz",
+      "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/compiler-core": "3.5.25",
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/compiler-ssr": "3.5.25",
+        "@vue/shared": "3.5.25",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.21",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz",
+      "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
+      "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-kit": "^7.7.9"
+      }
+    },
+    "node_modules/@vue/devtools-kit": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
+      "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-shared": "^7.7.9",
+        "birpc": "^2.3.0",
+        "hookable": "^5.5.3",
+        "mitt": "^3.0.1",
+        "perfect-debounce": "^1.0.0",
+        "speakingurl": "^14.0.1",
+        "superjson": "^2.2.2"
+      }
+    },
+    "node_modules/@vue/devtools-shared": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
+      "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
+      "license": "MIT",
+      "dependencies": {
+        "rfdc": "^1.4.1"
+      }
+    },
+    "node_modules/@vue/eslint-config-prettier": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz",
+      "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-config-prettier": "^10.0.1",
+        "eslint-plugin-prettier": "^5.2.2"
+      },
+      "peerDependencies": {
+        "eslint": ">= 8.21.0",
+        "prettier": ">= 3.0.0"
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.25.tgz",
+      "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.25.tgz",
+      "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz",
+      "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.25",
+        "@vue/runtime-core": "3.5.25",
+        "@vue/shared": "3.5.25",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.25.tgz",
+      "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.25",
+        "@vue/shared": "3.5.25"
+      },
+      "peerDependencies": {
+        "vue": "3.5.25"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz",
+      "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==",
+      "license": "MIT"
+    },
+    "node_modules/abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "event-target-shim": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6.5"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/accepts/node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.15.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+      "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-align": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+      "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.1.0"
+      }
+    },
+    "node_modules/ansi-escapes": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "type-fest": "^0.21.3"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-escapes/node_modules/type-fest": {
+      "version": "0.21.3",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/anymatch/node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/archiver": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
+      "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "archiver-utils": "^5.0.2",
+        "async": "^3.2.4",
+        "buffer-crc32": "^1.0.0",
+        "readable-stream": "^4.0.0",
+        "readdir-glob": "^1.1.2",
+        "tar-stream": "^3.0.0",
+        "zip-stream": "^6.0.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/archiver-utils": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
+      "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "glob": "^10.0.0",
+        "graceful-fs": "^4.2.0",
+        "is-stream": "^2.0.1",
+        "lazystream": "^1.0.0",
+        "lodash": "^4.17.15",
+        "normalize-path": "^3.0.0",
+        "readable-stream": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+      "license": "MIT"
+    },
+    "node_modules/async": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/autoprefixer": {
+      "version": "10.4.23",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
+      "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "browserslist": "^4.28.1",
+        "caniuse-lite": "^1.0.30001760",
+        "fraction.js": "^5.3.4",
+        "picocolors": "^1.1.1",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "bin": {
+        "autoprefixer": "bin/autoprefixer"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/axios": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
+      "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.4",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/b4a": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
+      "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "react-native-b4a": "*"
+      },
+      "peerDependenciesMeta": {
+        "react-native-b4a": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/bare-events": {
+      "version": "2.8.2",
+      "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
+      "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "bare-abort-controller": "*"
+      },
+      "peerDependenciesMeta": {
+        "bare-abort-controller": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "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"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/baseline-browser-mapping": {
+      "version": "2.9.7",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz",
+      "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "baseline-browser-mapping": "dist/cli.js"
+      }
+    },
+    "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==",
+      "license": "Unlicense",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/birpc": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
+      "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
+    "node_modules/bl/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.4",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
+      "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "content-type": "~1.0.5",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "~1.2.0",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.4.24",
+        "on-finished": "~2.4.1",
+        "qs": "~6.14.0",
+        "raw-body": "~2.5.3",
+        "type-is": "~1.6.18",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/body-parser/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/body-parser/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/boxen": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz",
+      "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-align": "^3.0.1",
+        "camelcase": "^7.0.1",
+        "chalk": "^5.2.0",
+        "cli-boxes": "^3.0.0",
+        "string-width": "^5.1.2",
+        "type-fest": "^2.13.0",
+        "widest-line": "^4.0.1",
+        "wrap-ansi": "^8.1.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/boxen/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/boxen/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/boxen/node_modules/chalk": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+      "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/boxen/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "license": "MIT"
+    },
+    "node_modules/boxen/node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "license": "MIT",
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/boxen/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/boxen/node_modules/type-fest": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+      "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=12.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/boxen/node_modules/wrap-ansi": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.1.0",
+        "string-width": "^5.0.1",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "big-integer": "^1.6.44"
+      },
+      "engines": {
+        "node": ">= 5.10.0"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+      "license": "MIT",
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.28.1",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+      "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "baseline-browser-mapping": "^2.9.0",
+        "caniuse-lite": "^1.0.30001759",
+        "electron-to-chromium": "^1.5.263",
+        "node-releases": "^2.0.27",
+        "update-browserslist-db": "^1.2.0"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "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"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "node_modules/buffer-builder": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz",
+      "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==",
+      "dev": true,
+      "license": "MIT/X11"
+    },
+    "node_modules/buffer-crc32": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
+      "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/bundle-name": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
+      "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "run-applescript": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/cacheable-lookup": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+      "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.16"
+      }
+    },
+    "node_modules/cacheable-request": {
+      "version": "10.2.14",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz",
+      "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-cache-semantics": "^4.0.2",
+        "get-stream": "^6.0.1",
+        "http-cache-semantics": "^4.1.1",
+        "keyv": "^4.5.3",
+        "mimic-response": "^4.0.0",
+        "normalize-url": "^8.0.0",
+        "responselike": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/camel-case": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+      "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pascal-case": "^3.1.2",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/camelcase": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
+      "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001760",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
+      "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "CC-BY-4.0"
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/chardet": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz",
+      "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/chart.js": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
+      "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
+      "license": "MIT",
+      "dependencies": {
+        "@kurkle/color": "^0.3.0"
+      },
+      "engines": {
+        "pnpm": ">=8"
+      }
+    },
+    "node_modules/chartjs-plugin-datalabels": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
+      "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "chart.js": ">=3.0.0"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/ci-info": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz",
+      "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/clean-css": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
+      "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "source-map": "~0.6.0"
+      },
+      "engines": {
+        "node": ">= 10.0"
+      }
+    },
+    "node_modules/cli-boxes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+      "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "restore-cursor": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cli-spinners": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+      "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-width": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
+      "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/cliui/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/clone": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/clone-deep": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-plain-object": "^2.0.4",
+        "kind-of": "^6.0.2",
+        "shallow-clone": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/colorjs.io": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
+      "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+      "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/compress-commons": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
+      "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "crc-32": "^1.2.0",
+        "crc32-stream": "^6.0.0",
+        "is-stream": "^2.0.1",
+        "normalize-path": "^3.0.0",
+        "readable-stream": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": ">= 1.43.0 < 2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/compression": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
+      "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "compressible": "~2.0.18",
+        "debug": "2.6.9",
+        "negotiator": "~0.6.4",
+        "on-headers": "~1.1.0",
+        "safe-buffer": "5.2.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/compression/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/compression/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/confbox": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+      "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/config-chain": {
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+      "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ini": "^1.3.4",
+        "proto-list": "~1.2.1"
+      }
+    },
+    "node_modules/config-chain/node_modules/ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "license": "ISC"
+    },
+    "node_modules/configstore": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz",
+      "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dot-prop": "^6.0.1",
+        "graceful-fs": "^4.2.6",
+        "unique-string": "^3.0.0",
+        "write-file-atomic": "^3.0.3",
+        "xdg-basedir": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/yeoman/configstore?sponsor=1"
+      }
+    },
+    "node_modules/configstore/node_modules/dot-prop": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
+      "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
+      "license": "MIT",
+      "dependencies": {
+        "is-obj": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/connect-history-api-fallback": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
+      "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+      "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+      "license": "MIT"
+    },
+    "node_modules/copy-anything": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
+      "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
+      "license": "MIT",
+      "dependencies": {
+        "is-what": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cors": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+      "license": "MIT",
+      "dependencies": {
+        "object-assign": "^4",
+        "vary": "^1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/crc32-stream": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz",
+      "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "crc-32": "^1.2.0",
+        "readable-stream": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/crypto-random-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz",
+      "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==",
+      "license": "MIT",
+      "dependencies": {
+        "type-fest": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/crypto-random-string/node_modules/type-fest": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+      "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "license": "MIT"
+    },
+    "node_modules/date-fns": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
+      "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/kossnocorp"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "license": "MIT",
+      "dependencies": {
+        "mimic-response": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/decompress-response/node_modules/mimic-response": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/deep-extend": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/default-browser": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz",
+      "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bundle-name": "^4.1.0",
+        "default-browser-id": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser-id": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
+      "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/defaults": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+      "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/defer-to-connect": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "bin": {
+        "detect-libc": "bin/detect-libc.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/dot-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+      "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/dot-prop": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
+      "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "type-fest": "^4.18.2"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "16.6.1",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+      "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz",
+      "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dotenv": "^16.4.5"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/eastasianwidth": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+      "license": "MIT"
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "license": "MIT"
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.267",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+      "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/elementtree": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz",
+      "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "sax": "1.1.4"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT"
+    },
+    "node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/engine.io-client": {
+      "version": "6.6.3",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
+      "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1",
+        "xmlhttprequest-ssl": "~2.1.1"
+      }
+    },
+    "node_modules/engine.io-client/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/engine.io-parser": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+      "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+      "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.25.12",
+        "@esbuild/android-arm": "0.25.12",
+        "@esbuild/android-arm64": "0.25.12",
+        "@esbuild/android-x64": "0.25.12",
+        "@esbuild/darwin-arm64": "0.25.12",
+        "@esbuild/darwin-x64": "0.25.12",
+        "@esbuild/freebsd-arm64": "0.25.12",
+        "@esbuild/freebsd-x64": "0.25.12",
+        "@esbuild/linux-arm": "0.25.12",
+        "@esbuild/linux-arm64": "0.25.12",
+        "@esbuild/linux-ia32": "0.25.12",
+        "@esbuild/linux-loong64": "0.25.12",
+        "@esbuild/linux-mips64el": "0.25.12",
+        "@esbuild/linux-ppc64": "0.25.12",
+        "@esbuild/linux-riscv64": "0.25.12",
+        "@esbuild/linux-s390x": "0.25.12",
+        "@esbuild/linux-x64": "0.25.12",
+        "@esbuild/netbsd-arm64": "0.25.12",
+        "@esbuild/netbsd-x64": "0.25.12",
+        "@esbuild/openbsd-arm64": "0.25.12",
+        "@esbuild/openbsd-x64": "0.25.12",
+        "@esbuild/openharmony-arm64": "0.25.12",
+        "@esbuild/sunos-x64": "0.25.12",
+        "@esbuild/win32-arm64": "0.25.12",
+        "@esbuild/win32-ia32": "0.25.12",
+        "@esbuild/win32-x64": "0.25.12"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-goat": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
+      "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "license": "MIT"
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/escodegen": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+      "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esprima": "^4.0.1",
+        "estraverse": "^5.2.0",
+        "esutils": "^2.0.2"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "optionalDependencies": {
+        "source-map": "~0.6.1"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "9.39.2",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+      "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.8.0",
+        "@eslint-community/regexpp": "^4.12.1",
+        "@eslint/config-array": "^0.21.1",
+        "@eslint/config-helpers": "^0.4.2",
+        "@eslint/core": "^0.17.0",
+        "@eslint/eslintrc": "^3.3.1",
+        "@eslint/js": "9.39.2",
+        "@eslint/plugin-kit": "^0.4.1",
+        "@humanfs/node": "^0.16.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@humanwhocodes/retry": "^0.4.2",
+        "@types/estree": "^1.0.6",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.2",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^8.4.0",
+        "eslint-visitor-keys": "^4.2.1",
+        "espree": "^10.4.0",
+        "esquery": "^1.5.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^8.0.0",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-compat-utils": {
+      "version": "0.6.5",
+      "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.5.tgz",
+      "integrity": "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "eslint": ">=6.0.0"
+      }
+    },
+    "node_modules/eslint-config-prettier": {
+      "version": "10.1.8",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+      "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "eslint-config-prettier": "bin/cli.js"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-config-prettier"
+      },
+      "peerDependencies": {
+        "eslint": ">=7.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-prettier": {
+      "version": "5.5.4",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz",
+      "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prettier-linter-helpers": "^1.0.0",
+        "synckit": "^0.11.7"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-plugin-prettier"
+      },
+      "peerDependencies": {
+        "@types/eslint": ">=8.0.0",
+        "eslint": ">=8.0.0",
+        "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
+        "prettier": ">=3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/eslint": {
+          "optional": true
+        },
+        "eslint-config-prettier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-plugin-prettier/node_modules/synckit": {
+      "version": "0.11.11",
+      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
+      "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@pkgr/core": "^0.2.9"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/synckit"
+      }
+    },
+    "node_modules/eslint-plugin-vue": {
+      "version": "10.6.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.6.2.tgz",
+      "integrity": "sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "natural-compare": "^1.4.0",
+        "nth-check": "^2.1.1",
+        "postcss-selector-parser": "^7.1.0",
+        "semver": "^7.6.3",
+        "xml-name-validator": "^4.0.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "peerDependencies": {
+        "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
+        "@typescript-eslint/parser": "^7.0.0 || ^8.0.0",
+        "eslint": "^8.57.0 || ^9.0.0",
+        "vue-eslint-parser": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@stylistic/eslint-plugin": {
+          "optional": true
+        },
+        "@typescript-eslint/parser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/eslint/node_modules/ignore": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/espree": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+      "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/espree/node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+      "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/events-universal": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+      "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bare-events": "^2.7.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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/execa/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==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/execa/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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/execa/node_modules/npm-run-path": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+      "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+      "license": "MIT",
+      "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/execa/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==",
+      "license": "MIT",
+      "dependencies": {
+        "mimic-fn": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/execa/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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/execa/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==",
+      "license": "ISC"
+    },
+    "node_modules/express": {
+      "version": "4.22.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+      "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "~1.20.3",
+        "content-disposition": "~0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "~0.7.1",
+        "cookie-signature": "~1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.3.1",
+        "fresh": "~0.5.2",
+        "http-errors": "~2.0.0",
+        "merge-descriptors": "1.0.3",
+        "methods": "~1.1.2",
+        "on-finished": "~2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "~0.1.12",
+        "proxy-addr": "~2.0.7",
+        "qs": "~6.14.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "~0.19.0",
+        "serve-static": "~1.16.2",
+        "setprototypeof": "1.2.0",
+        "statuses": "~2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/express/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/express/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "license": "MIT"
+    },
+    "node_modules/fast-diff": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+      "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/fast-fifo": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+      "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-glob": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.8"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fastq": {
+      "version": "1.19.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+      "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+      "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+      "license": "MIT",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+      "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "~2.0.2",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/finalhandler/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/finalhandler/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "bin": {
+        "flat": "cli.js"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+      "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+      "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/foreground-child": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+      "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "cross-spawn": "^7.0.6",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/form-data-encoder": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
+      "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.17"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fraction.js": {
+      "version": "5.3.4",
+      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+      "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/rawify"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fs-extra": {
+      "version": "11.3.2",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz",
+      "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==",
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/glob": {
+      "version": "10.5.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+      "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/glob/node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/glob/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/global-dirs": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz",
+      "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==",
+      "license": "MIT",
+      "dependencies": {
+        "ini": "2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/got": {
+      "version": "12.6.1",
+      "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz",
+      "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/is": "^5.2.0",
+        "@szmarczak/http-timer": "^5.0.1",
+        "cacheable-lookup": "^7.0.0",
+        "cacheable-request": "^10.2.8",
+        "decompress-response": "^6.0.0",
+        "form-data-encoder": "^2.1.2",
+        "get-stream": "^6.0.1",
+        "http2-wrapper": "^2.1.10",
+        "lowercase-keys": "^3.0.0",
+        "p-cancelable": "^3.0.0",
+        "responselike": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/got?sponsor=1"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "license": "ISC"
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-yarn": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz",
+      "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hookable": {
+      "version": "5.5.3",
+      "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
+      "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+      "license": "MIT"
+    },
+    "node_modules/html-minifier-terser": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
+      "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "camel-case": "^4.1.2",
+        "clean-css": "~5.3.2",
+        "commander": "^10.0.0",
+        "entities": "^4.4.0",
+        "param-case": "^3.0.4",
+        "relateurl": "^0.2.7",
+        "terser": "^5.15.1"
+      },
+      "bin": {
+        "html-minifier-terser": "cli.js"
+      },
+      "engines": {
+        "node": "^14.13.1 || >=16.0.0"
+      }
+    },
+    "node_modules/http-cache-semantics": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+      "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~2.0.0",
+        "inherits": "~2.0.4",
+        "setprototypeof": "~1.2.0",
+        "statuses": "~2.0.2",
+        "toidentifier": "~1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/http-proxy": {
+      "version": "1.18.1",
+      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+      "license": "MIT",
+      "dependencies": {
+        "eventemitter3": "^4.0.0",
+        "follow-redirects": "^1.0.0",
+        "requires-port": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/http-proxy-middleware": {
+      "version": "2.0.9",
+      "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
+      "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-proxy": "^1.17.8",
+        "http-proxy": "^1.18.1",
+        "is-glob": "^4.0.1",
+        "is-plain-obj": "^3.0.0",
+        "micromatch": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "@types/express": "^4.17.13"
+      },
+      "peerDependenciesMeta": {
+        "@types/express": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/http2-wrapper": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
+      "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
+      "license": "MIT",
+      "dependencies": {
+        "quick-lru": "^5.1.1",
+        "resolve-alpn": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=10.19.0"
+      }
+    },
+    "node_modules/human-signals": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
+      "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14.18.0"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "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"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/immutable": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz",
+      "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/import-lazy": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz",
+      "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/ini": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+      "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/inquirer": {
+      "version": "9.3.8",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.3.8.tgz",
+      "integrity": "sha512-pFGGdaHrmRKMh4WoDDSowddgjT1Vkl90atobmTeSmcPGdYiwikch/m/Ef5wRaiamHejtw0cUUMMerzDUXCci2w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/external-editor": "^1.0.2",
+        "@inquirer/figures": "^1.0.3",
+        "ansi-escapes": "^4.3.2",
+        "cli-width": "^4.1.0",
+        "mute-stream": "1.0.0",
+        "ora": "^5.4.1",
+        "run-async": "^3.0.0",
+        "rxjs": "^7.8.1",
+        "string-width": "^4.2.3",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^6.2.0",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-ci": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
+      "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ci-info": "^3.2.0"
+      },
+      "bin": {
+        "is-ci": "bin.js"
+      }
+    },
+    "node_modules/is-ci/node_modules/ci-info": {
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+      "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "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==",
+      "license": "MIT",
+      "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-installed-globally": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
+      "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
+      "license": "MIT",
+      "dependencies": {
+        "global-dirs": "^3.0.0",
+        "is-path-inside": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-interactive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-language-code": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/is-language-code/-/is-language-code-3.1.0.tgz",
+      "integrity": "sha512-zJdQ3QTeLye+iphMeK3wks+vXSRFKh68/Pnlw7aOfApFSEIOhYa8P9vwwa6QrImNNBMJTiL1PpYF0f4BxDuEgA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.14.0"
+      }
+    },
+    "node_modules/is-npm": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.1.0.tgz",
+      "integrity": "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-obj": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-plain-obj": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
+      "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+      "license": "MIT"
+    },
+    "node_modules/is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-what": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
+      "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/is-wsl": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
+      "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-inside-container": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-yarn-global": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz",
+      "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/isbinaryfile": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
+      "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/gjtorikian/"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "license": "ISC"
+    },
+    "node_modules/isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/jackspeak": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+      "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/cliui": "^8.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      },
+      "optionalDependencies": {
+        "@pkgjs/parseargs": "^0.11.0"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+      "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsonc-eslint-parser": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.2.tgz",
+      "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.5.0",
+        "eslint-visitor-keys": "^3.0.0",
+        "espree": "^9.0.0",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ota-meshi"
+      }
+    },
+    "node_modules/jsonc-eslint-parser/node_modules/espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "license": "MIT",
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/kolorist": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
+      "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
+      "license": "MIT"
+    },
+    "node_modules/latest-version": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz",
+      "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==",
+      "license": "MIT",
+      "dependencies": {
+        "package-json": "^8.1.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lazystream": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+      "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "readable-stream": "^2.0.5"
+      },
+      "engines": {
+        "node": ">= 0.6.3"
+      }
+    },
+    "node_modules/lazystream/node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/lazystream/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lazystream/node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lower-case": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/lowercase-keys": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+      "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/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==",
+      "license": "MIT"
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+      "license": "MIT",
+      "dependencies": {
+        "braces": "^3.0.3",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/micromatch/node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/mimic-response": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
+      "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "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",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/mitt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+      "license": "MIT"
+    },
+    "node_modules/mlly": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
+      "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "pathe": "^2.0.3",
+        "pkg-types": "^1.3.1",
+        "ufo": "^1.6.1"
+      }
+    },
+    "node_modules/mlly/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/mute-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
+      "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/negotiator": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
+      "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/no-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "lower-case": "^2.0.2",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/node-addon-api": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+      "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/node-forge": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz",
+      "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
+      "license": "(BSD-3-Clause OR GPL-2.0)",
+      "engines": {
+        "node": ">= 6.13.0"
+      }
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.27",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+      "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/normalize-url": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.0.tgz",
+      "integrity": "sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/npm-run-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
+      "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^4.0.0",
+        "unicorn-magic": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "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,
+      "license": "MIT",
+      "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",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/on-headers": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+      "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "license": "MIT",
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/open": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
+      "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "default-browser": "^5.2.1",
+        "define-lazy-prop": "^3.0.0",
+        "is-inside-container": "^1.0.0",
+        "wsl-utils": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/ora": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bl": "^4.1.0",
+        "chalk": "^4.1.0",
+        "cli-cursor": "^3.1.0",
+        "cli-spinners": "^2.5.0",
+        "is-interactive": "^1.0.0",
+        "is-unicode-supported": "^0.1.0",
+        "log-symbols": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "wcwidth": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-cancelable": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+      "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.20"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/package-json": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz",
+      "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==",
+      "license": "MIT",
+      "dependencies": {
+        "got": "^12.1.0",
+        "registry-auth-token": "^5.0.1",
+        "registry-url": "^6.0.0",
+        "semver": "^7.3.7"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/package-json-from-dist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+      "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0"
+    },
+    "node_modules/param-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+      "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "dot-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+      "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5/node_modules/entities": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/pascal-case": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-scurry": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^10.2.0",
+        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+      "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+      "license": "MIT"
+    },
+    "node_modules/pathe": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+      "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/perfect-debounce": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+      "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+      "license": "MIT"
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pinia": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
+      "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^7.7.7"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.5.0",
+        "vue": "^3.5.11"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pkg-types": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+      "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "confbox": "^0.1.8",
+        "mlly": "^1.7.4",
+        "pathe": "^2.0.1"
+      }
+    },
+    "node_modules/pkg-types/node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
+      "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss-value-parser": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/prettier": {
+      "version": "3.7.4",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
+      "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "prettier": "bin/prettier.cjs"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-diff": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/proto-list": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+      "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+      "license": "ISC"
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "license": "MIT",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
+      "license": "ISC"
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/pupa": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz",
+      "integrity": "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==",
+      "license": "MIT",
+      "dependencies": {
+        "escape-goat": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/quasar": {
+      "version": "2.18.6",
+      "resolved": "https://registry.npmjs.org/quasar/-/quasar-2.18.6.tgz",
+      "integrity": "sha512-ZlK+vJXOBPSFDCNQDBDNwSI+AHoqaFPxK8ve6mhsYLhMKWI5b8zsGY9VU1xYjngO2aBvU4fvGWXy4tTbzrBk8Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.18.1",
+        "npm": ">= 6.13.4",
+        "yarn": ">= 1.21.1"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://donate.quasar.dev"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "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"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/quick-lru": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+      "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.4.24",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/rc": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+      "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+      "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+      "dependencies": {
+        "deep-extend": "^0.6.0",
+        "ini": "~1.3.0",
+        "minimist": "^1.2.0",
+        "strip-json-comments": "~2.0.1"
+      },
+      "bin": {
+        "rc": "cli.js"
+      }
+    },
+    "node_modules/rc/node_modules/ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "license": "ISC"
+    },
+    "node_modules/rc/node_modules/strip-json-comments": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+      "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "abort-controller": "^3.0.0",
+        "buffer": "^6.0.3",
+        "events": "^3.3.0",
+        "process": "^0.11.10",
+        "string_decoder": "^1.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/readable-stream/node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "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"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/readdir-glob": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+      "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "minimatch": "^5.1.0"
+      }
+    },
+    "node_modules/readdir-glob/node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/readdir-glob/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/readdirp/node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/registry-auth-token": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz",
+      "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==",
+      "license": "MIT",
+      "dependencies": {
+        "@pnpm/npm-conf": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/registry-url": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz",
+      "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==",
+      "license": "MIT",
+      "dependencies": {
+        "rc": "1.2.8"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/relateurl": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+      "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/requires-port": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+      "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+      "license": "MIT"
+    },
+    "node_modules/resolve-alpn": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
+      "license": "MIT"
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/responselike": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
+      "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
+      "license": "MIT",
+      "dependencies": {
+        "lowercase-keys": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/restore-cursor/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,
+      "license": "ISC"
+    },
+    "node_modules/reusify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+      "license": "MIT"
+    },
+    "node_modules/rollup": {
+      "version": "4.53.5",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.5.tgz",
+      "integrity": "sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.8"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.53.5",
+        "@rollup/rollup-android-arm64": "4.53.5",
+        "@rollup/rollup-darwin-arm64": "4.53.5",
+        "@rollup/rollup-darwin-x64": "4.53.5",
+        "@rollup/rollup-freebsd-arm64": "4.53.5",
+        "@rollup/rollup-freebsd-x64": "4.53.5",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.53.5",
+        "@rollup/rollup-linux-arm-musleabihf": "4.53.5",
+        "@rollup/rollup-linux-arm64-gnu": "4.53.5",
+        "@rollup/rollup-linux-arm64-musl": "4.53.5",
+        "@rollup/rollup-linux-loong64-gnu": "4.53.5",
+        "@rollup/rollup-linux-ppc64-gnu": "4.53.5",
+        "@rollup/rollup-linux-riscv64-gnu": "4.53.5",
+        "@rollup/rollup-linux-riscv64-musl": "4.53.5",
+        "@rollup/rollup-linux-s390x-gnu": "4.53.5",
+        "@rollup/rollup-linux-x64-gnu": "4.53.5",
+        "@rollup/rollup-linux-x64-musl": "4.53.5",
+        "@rollup/rollup-openharmony-arm64": "4.53.5",
+        "@rollup/rollup-win32-arm64-msvc": "4.53.5",
+        "@rollup/rollup-win32-ia32-msvc": "4.53.5",
+        "@rollup/rollup-win32-x64-gnu": "4.53.5",
+        "@rollup/rollup-win32-x64-msvc": "4.53.5",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/rollup-plugin-visualizer": {
+      "version": "5.14.0",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.14.0.tgz",
+      "integrity": "sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "open": "^8.4.0",
+        "picomatch": "^4.0.2",
+        "source-map": "^0.7.4",
+        "yargs": "^17.5.1"
+      },
+      "bin": {
+        "rollup-plugin-visualizer": "dist/bin/cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "rolldown": "1.x",
+        "rollup": "2.x || 3.x || 4.x"
+      },
+      "peerDependenciesMeta": {
+        "rolldown": {
+          "optional": true
+        },
+        "rollup": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/rollup-plugin-visualizer/node_modules/define-lazy-prop": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+      "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/rollup-plugin-visualizer/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,
+      "license": "MIT",
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/rollup-plugin-visualizer/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,
+      "license": "MIT",
+      "dependencies": {
+        "is-docker": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/rollup-plugin-visualizer/node_modules/open": {
+      "version": "8.4.2",
+      "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
+      "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "define-lazy-prop": "^2.0.0",
+        "is-docker": "^2.1.1",
+        "is-wsl": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/rollup-plugin-visualizer/node_modules/source-map": {
+      "version": "0.7.6",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+      "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/route-cache": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/route-cache/-/route-cache-0.5.0.tgz",
+      "integrity": "sha512-7FzV+1O4q7XeerbyG8aEeDH+1bk/Vxp2sDJdEZE0KcbTP0C6IucKSQUCTwB3F0IkhpF4rYluLLENEfUQ6LH/ng==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "3.1.0",
+        "lru-cache": "4.0.1"
+      }
+    },
+    "node_modules/route-cache/node_modules/debug": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+      "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/route-cache/node_modules/lru-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.1.tgz",
+      "integrity": "sha512-MX0ZnRoVTWXBiNe9dysqKXjvhmQgHsOirh/2rerIVJ8sbQeMxc5OPj0HDpVV3bYjdE6GTHrPf8BEHJqWHFkjHA==",
+      "license": "ISC",
+      "dependencies": {
+        "pseudomap": "^1.0.1",
+        "yallist": "^2.0.0"
+      }
+    },
+    "node_modules/route-cache/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/run-applescript": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
+      "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/run-async": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
+      "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "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"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/rxjs": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+      "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/sass": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.0.tgz",
+      "integrity": "sha512-KR0igP1z4avUJetEuIeOdDlwaUDvkH8wSx7FdSjyYBS3dpyX3TzHfAMO0G1Q4/3cdjcmi3r7idh+KCmKqS+KeQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "chokidar": "^4.0.0",
+        "immutable": "^5.0.2",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher": "^2.4.1"
+      }
+    },
+    "node_modules/sass-embedded": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.97.0.tgz",
+      "integrity": "sha512-Unwu0MtlAt9hQGHutB2NJhwhPcxiJX99AI7PSz7W4lkikQg9S/HYFtgxtIjpTB4DW7sOYX2xnxvtU/nep9HXTA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@bufbuild/protobuf": "^2.5.0",
+        "buffer-builder": "^0.2.0",
+        "colorjs.io": "^0.5.0",
+        "immutable": "^5.0.2",
+        "rxjs": "^7.4.0",
+        "supports-color": "^8.1.1",
+        "sync-child-process": "^1.0.2",
+        "varint": "^6.0.0"
+      },
+      "bin": {
+        "sass": "dist/bin/sass.js"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      },
+      "optionalDependencies": {
+        "sass-embedded-all-unknown": "1.97.0",
+        "sass-embedded-android-arm": "1.97.0",
+        "sass-embedded-android-arm64": "1.97.0",
+        "sass-embedded-android-riscv64": "1.97.0",
+        "sass-embedded-android-x64": "1.97.0",
+        "sass-embedded-darwin-arm64": "1.97.0",
+        "sass-embedded-darwin-x64": "1.97.0",
+        "sass-embedded-linux-arm": "1.97.0",
+        "sass-embedded-linux-arm64": "1.97.0",
+        "sass-embedded-linux-musl-arm": "1.97.0",
+        "sass-embedded-linux-musl-arm64": "1.97.0",
+        "sass-embedded-linux-musl-riscv64": "1.97.0",
+        "sass-embedded-linux-musl-x64": "1.97.0",
+        "sass-embedded-linux-riscv64": "1.97.0",
+        "sass-embedded-linux-x64": "1.97.0",
+        "sass-embedded-unknown-all": "1.97.0",
+        "sass-embedded-win32-arm64": "1.97.0",
+        "sass-embedded-win32-x64": "1.97.0"
+      }
+    },
+    "node_modules/sass-embedded-all-unknown": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.97.0.tgz",
+      "integrity": "sha512-9F6MyQcwp3YiuGMk5bC7g9jL+D1KkW/ONQgrkoTQ7ALcmoPKmsauZg5WgRhLYW9UhpnGTgANrWrZdiREAR1YkA==",
+      "cpu": [
+        "!arm",
+        "!arm64",
+        "!riscv64",
+        "!x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "sass": "1.97.0"
+      }
+    },
+    "node_modules/sass-embedded-android-arm": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.97.0.tgz",
+      "integrity": "sha512-VLxeVR5FMwSZoOliBY8Qy2trZCWYz3w4ILf0QZ68eep3mIQjtykY3BSKC2R/w9DkPQDNJXdgbgnxeOubC8k5xw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-android-arm64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.97.0.tgz",
+      "integrity": "sha512-uDG/0DS6A+KRiOYUV1UNHBq67DHvO+/54Ja+dg8S5fl5uvPwZGHpJFheemA9R6vvddwyjGmzVacvCQxdmECcfQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-android-riscv64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.97.0.tgz",
+      "integrity": "sha512-yrwsyPR08CXW5Ggr0kI1jTUcKkBOtjODbDj11nRrBwyrXRqhf1obqfchQxTW0HlYT8VZmZGfnHvPNNDwOSdfZg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-android-x64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.97.0.tgz",
+      "integrity": "sha512-a1QW1pFykLCtV8J3AZ+wtrwOx0ORZsW4orF6fOrBYL2sLhlzhB3iK+QzWezFvH5+FMgLQBC4xgYYk4NV9WCO9g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-darwin-arm64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.97.0.tgz",
+      "integrity": "sha512-5XV42FEqhQEGFQ/w8HUk///k0XMHLyBt1j2alxTr9ZI77HqiAIl6kVZp0kxJ++gt/y3E6hKoMLngHHC6zIBR5A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-darwin-x64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.97.0.tgz",
+      "integrity": "sha512-Kc0aKFfKPd/kz8mSGtRKTEN7FKnqs30iZf6APb0ZHMuvMVfOfdD+fZ/85htT+j5k2F+UUSFBpbx04W0gZW020A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-linux-arm": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.97.0.tgz",
+      "integrity": "sha512-pwM5A1+w3l1T/FXwJNqZD0WukCENeRkgxPSpZmsO4/QNLdTpGCz16D5spYPQ7f7GZo9aNaHt1EaDLHCjlEA8LQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-linux-arm64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.97.0.tgz",
+      "integrity": "sha512-ofm9esPA9P0sB6wJPcDhQYjSDfa7RoVKD0IHvFPMrK9OLTKg8lw80/afH49a9URYeYiE4wFP76Fr9t+s7A6E1Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-linux-musl-arm": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.97.0.tgz",
+      "integrity": "sha512-+rsW0OreW4sPtdXDewDESxJLJdxW3B0EL7ICajkRFs3KbeNdgOVnP5DJQ39hquAoZH0AcEEGcd6236ZMMzEbwQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-linux-musl-arm64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.97.0.tgz",
+      "integrity": "sha512-8VF4nc7oUklhUGGAY0T6Ktd9T9ZFwoOsWje7ocOV57tjbocFp/eeAPqX1v2BpiZtMVURyYwaZsRSAL79DT7oRw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-linux-musl-riscv64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.97.0.tgz",
+      "integrity": "sha512-nlaeeZ5P7tde/c/aMiIl5UduQZPA9ftEyWJxdmWcs3pASFSykslVJR5D4L161EUHzB5z+MxSnbbzcrck0F1slA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-linux-musl-x64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.97.0.tgz",
+      "integrity": "sha512-QB6JLr2p1UuEXhiTXEYNypf+w2x/SCMY17vcnXKM47CeaJ88v2C9fJ9oVne6eZntlCylSow/vZCov0JMhklknA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-linux-riscv64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.97.0.tgz",
+      "integrity": "sha512-m7QaK4M+YhQ6FZWMI9O8g4tqmM4JrvzJl/YC/eEJXpfgwxMeXsDsPVQWFiBdWOuxqMSH8WhFksw/Bg0J+kK6VQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-linux-x64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.97.0.tgz",
+      "integrity": "sha512-yc7yLWJrAtTBCjEAoNxvE040EGYdsgmaWMSyI9LSIOFlSwrOc4x+W/8IMhLWCygTAgorNPuNlRfPDgkQm1sJmw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-unknown-all": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.97.0.tgz",
+      "integrity": "sha512-dDky3ETKeOo543myScL4sp3pj2cANLNKea5aR6v8ZCpDSCDTRxqv4Sj/goTmkVqnp/HOVF88qB3GHtQ8rFtULQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "!android",
+        "!darwin",
+        "!linux",
+        "!win32"
+      ],
+      "dependencies": {
+        "sass": "1.97.0"
+      }
+    },
+    "node_modules/sass-embedded-win32-arm64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.97.0.tgz",
+      "integrity": "sha512-JMU2SKIgUJDw4oaKBcVbuobWRU6f2XmFuYqJdkxJhlITAGimwjZ860gttlzjNtZcVI4+p4ovT14HwpsEcIzfnw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded-win32-x64": {
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.97.0.tgz",
+      "integrity": "sha512-mKIJGXxEl6OoWEoT4ee5OsBOfExla2ilY5J8tupVwSCxf/i3aOJNLm7ZzRWG9er2K3bC8aovgMisMIVGlBM5hw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-embedded/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/sass/node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/sass/node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/sax": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz",
+      "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/selfsigned": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz",
+      "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node-forge": "^1.3.0",
+        "node-forge": "^1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/semver": {
+      "version": "7.7.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+      "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/semver-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz",
+      "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==",
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/send": {
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+      "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "~0.5.2",
+        "http-errors": "~2.0.1",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "~2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "~2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/send/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/send/node_modules/debug/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "node_modules/serve-static": {
+      "version": "1.16.3",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+      "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+      "license": "MIT",
+      "dependencies": {
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "~0.19.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "node_modules/shallow-clone": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "kind-of": "^6.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+      "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/socket.io-client": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
+      "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.2",
+        "engine.io-client": "~6.6.1",
+        "socket.io-parser": "~4.2.4"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-client/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/socket.io-parser": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+      "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-parser/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/speakingurl": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
+      "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stack-trace": {
+      "version": "1.0.0-pre2",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-1.0.0-pre2.tgz",
+      "integrity": "sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/streamx": {
+      "version": "2.23.0",
+      "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
+      "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "events-universal": "^1.0.0",
+        "fast-fifo": "^1.3.2",
+        "text-decoder": "^1.1.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width-cjs": {
+      "name": "string-width",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi-cjs": {
+      "name": "strip-ansi",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "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==",
+      "license": "MIT",
+      "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",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/superjson": {
+      "version": "2.2.6",
+      "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
+      "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
+      "license": "MIT",
+      "dependencies": {
+        "copy-anything": "^4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/sync-child-process": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
+      "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "sync-message-port": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/sync-message-port": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz",
+      "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/synckit": {
+      "version": "0.10.4",
+      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.10.4.tgz",
+      "integrity": "sha512-2SG1TnJGjMkD4+gblONMGYSrwAzYi+ymOitD+Jb/iMYm57nH20PlkVeMQRah3yDMKEa0QQYUF/QPWpdW7C6zNg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@pkgr/core": "^0.2.0",
+        "tslib": "^2.8.1"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unts"
+      }
+    },
+    "node_modules/tar-stream": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+      "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "b4a": "^1.6.4",
+        "fast-fifo": "^1.2.0",
+        "streamx": "^2.15.0"
+      }
+    },
+    "node_modules/terser": {
+      "version": "5.44.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
+      "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.15.0",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/text-decoder": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
+      "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "b4a": "^1.6.4"
+      }
+    },
+    "node_modules/tiny-invariant": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+      "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinyglobby": {
+      "version": "0.2.15",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+      "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
+    "node_modules/titleize": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
+      "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
+      "license": "MIT",
+      "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",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "license": "MIT",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/ts-api-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+      "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.12"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4"
+      }
+    },
+    "node_modules/ts-essentials": {
+      "version": "9.4.2",
+      "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.4.2.tgz",
+      "integrity": "sha512-mB/cDhOvD7pg3YCLk2rOtejHjjdSi9in/IBYE13S+8WA5FBSraYf4V/ws55uvs0IvQ/l0wBOlXy5yBNZ9Bl8ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "typescript": ">=4.1.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "dev": true,
+      "license": "0BSD"
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "4.41.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+      "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "license": "MIT",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typedarray-to-buffer": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+      "license": "MIT",
+      "dependencies": {
+        "is-typedarray": "^1.0.0"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+      "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+      "devOptional": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/ufo": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
+      "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/undici-types": {
+      "version": "7.16.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+      "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+      "license": "MIT"
+    },
+    "node_modules/unicorn-magic": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+      "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/unique-string": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz",
+      "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==",
+      "license": "MIT",
+      "dependencies": {
+        "crypto-random-string": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/unplugin": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz",
+      "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.14.0",
+        "webpack-virtual-modules": "^0.6.2"
+      },
+      "engines": {
+        "node": ">=14.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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
+      "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.1"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/update-notifier": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz",
+      "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boxen": "^7.0.0",
+        "chalk": "^5.0.1",
+        "configstore": "^6.0.0",
+        "has-yarn": "^3.0.0",
+        "import-lazy": "^4.0.0",
+        "is-ci": "^3.0.1",
+        "is-installed-globally": "^0.4.0",
+        "is-npm": "^6.0.0",
+        "is-yarn-global": "^0.4.0",
+        "latest-version": "^7.0.0",
+        "pupa": "^3.1.0",
+        "semver": "^7.3.7",
+        "semver-diff": "^4.0.0",
+        "xdg-basedir": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/yeoman/update-notifier?sponsor=1"
+      }
+    },
+    "node_modules/update-notifier/node_modules/chalk": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+      "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/varint": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
+      "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/vite": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
+      "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.27.0",
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.3",
+        "postcss": "^8.5.6",
+        "rollup": "^4.43.0",
+        "tinyglobby": "^0.2.15"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^20.19.0 || >=22.12.0",
+        "jiti": ">=1.21.0",
+        "less": "^4.0.0",
+        "lightningcss": "^1.21.0",
+        "sass": "^1.70.0",
+        "sass-embedded": "^1.70.0",
+        "stylus": ">=0.54.8",
+        "sugarss": "^5.0.0",
+        "terser": "^5.16.0",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "jiti": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-plugin-checker": {
+      "version": "0.9.3",
+      "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.9.3.tgz",
+      "integrity": "sha512-Tf7QBjeBtG7q11zG0lvoF38/2AVUzzhMNu+Wk+mcsJ00Rk/FpJ4rmUviVJpzWkagbU13cGXvKpt7CMiqtxVTbQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.27.1",
+        "chokidar": "^4.0.3",
+        "npm-run-path": "^6.0.0",
+        "picocolors": "^1.1.1",
+        "picomatch": "^4.0.2",
+        "strip-ansi": "^7.1.0",
+        "tiny-invariant": "^1.3.3",
+        "tinyglobby": "^0.2.13",
+        "vscode-uri": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "peerDependencies": {
+        "@biomejs/biome": ">=1.7",
+        "eslint": ">=7",
+        "meow": "^13.2.0",
+        "optionator": "^0.9.4",
+        "stylelint": ">=16",
+        "typescript": "*",
+        "vite": ">=2.0.0",
+        "vls": "*",
+        "vti": "*",
+        "vue-tsc": "~2.2.10"
+      },
+      "peerDependenciesMeta": {
+        "@biomejs/biome": {
+          "optional": true
+        },
+        "eslint": {
+          "optional": true
+        },
+        "meow": {
+          "optional": true
+        },
+        "optionator": {
+          "optional": true
+        },
+        "stylelint": {
+          "optional": true
+        },
+        "typescript": {
+          "optional": true
+        },
+        "vls": {
+          "optional": true
+        },
+        "vti": {
+          "optional": true
+        },
+        "vue-tsc": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-plugin-checker/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/vite-plugin-checker/node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/vite-plugin-checker/node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/vite-plugin-checker/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz",
+      "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-arm": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz",
+      "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-arm64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz",
+      "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-x64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz",
+      "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz",
+      "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz",
+      "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz",
+      "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz",
+      "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-arm": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz",
+      "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz",
+      "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz",
+      "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz",
+      "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz",
+      "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz",
+      "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz",
+      "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz",
+      "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-x64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz",
+      "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz",
+      "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz",
+      "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz",
+      "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz",
+      "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz",
+      "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz",
+      "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz",
+      "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz",
+      "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-x64": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz",
+      "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/vite/node_modules/esbuild": {
+      "version": "0.27.1",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz",
+      "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.27.1",
+        "@esbuild/android-arm": "0.27.1",
+        "@esbuild/android-arm64": "0.27.1",
+        "@esbuild/android-x64": "0.27.1",
+        "@esbuild/darwin-arm64": "0.27.1",
+        "@esbuild/darwin-x64": "0.27.1",
+        "@esbuild/freebsd-arm64": "0.27.1",
+        "@esbuild/freebsd-x64": "0.27.1",
+        "@esbuild/linux-arm": "0.27.1",
+        "@esbuild/linux-arm64": "0.27.1",
+        "@esbuild/linux-ia32": "0.27.1",
+        "@esbuild/linux-loong64": "0.27.1",
+        "@esbuild/linux-mips64el": "0.27.1",
+        "@esbuild/linux-ppc64": "0.27.1",
+        "@esbuild/linux-riscv64": "0.27.1",
+        "@esbuild/linux-s390x": "0.27.1",
+        "@esbuild/linux-x64": "0.27.1",
+        "@esbuild/netbsd-arm64": "0.27.1",
+        "@esbuild/netbsd-x64": "0.27.1",
+        "@esbuild/openbsd-arm64": "0.27.1",
+        "@esbuild/openbsd-x64": "0.27.1",
+        "@esbuild/openharmony-arm64": "0.27.1",
+        "@esbuild/sunos-x64": "0.27.1",
+        "@esbuild/win32-arm64": "0.27.1",
+        "@esbuild/win32-ia32": "0.27.1",
+        "@esbuild/win32-x64": "0.27.1"
+      }
+    },
+    "node_modules/vscode-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
+      "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/vue": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz",
+      "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/compiler-sfc": "3.5.25",
+        "@vue/runtime-dom": "3.5.25",
+        "@vue/server-renderer": "3.5.25",
+        "@vue/shared": "3.5.25"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-chartjs": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.3.tgz",
+      "integrity": "sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==",
+      "license": "MIT",
+      "peerDependencies": {
+        "chart.js": "^4.1.1",
+        "vue": "^3.0.0-0 || ^2.7.0"
+      }
+    },
+    "node_modules/vue-currency-input": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/vue-currency-input/-/vue-currency-input-3.2.1.tgz",
+      "integrity": "sha512-Osfxzdu5cdZSCS4Cm0vuk7LwNeSdHWGIWK8gtDBC1kU0UtAKz7iU/8dyJ0KDJKxbAYiKeovoQTRfYxCH82I0EA==",
+      "license": "MIT",
+      "peerDependencies": {
+        "vue": "^2.7 || ^3.0.0"
+      }
+    },
+    "node_modules/vue-eslint-parser": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz",
+      "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "debug": "^4.4.0",
+        "eslint-scope": "^8.2.0",
+        "eslint-visitor-keys": "^4.2.0",
+        "espree": "^10.3.0",
+        "esquery": "^1.6.0",
+        "semver": "^7.6.3"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0"
+      }
+    },
+    "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/vue-i18n": {
+      "version": "11.2.2",
+      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.2.2.tgz",
+      "integrity": "sha512-ULIKZyRluUPRCZmihVgUvpq8hJTtOqnbGZuv4Lz+byEKZq4mU0g92og414l6f/4ju+L5mORsiUuEPYrAuX2NJg==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/core-base": "11.2.2",
+        "@intlify/shared": "11.2.2",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/vue-i18n/node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
+    },
+    "node_modules/vue-router": {
+      "version": "4.6.4",
+      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
+      "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "vue": "^3.5.0"
+      }
+    },
+    "node_modules/vue-router/node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
+    },
+    "node_modules/wcwidth": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "node_modules/webpack-merge": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz",
+      "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone-deep": "^4.0.1",
+        "flat": "^5.0.2",
+        "wildcard": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/webpack-virtual-modules": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
+      "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/widest-line": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
+      "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
+      "license": "MIT",
+      "dependencies": {
+        "string-width": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/widest-line/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/widest-line/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "license": "MIT"
+    },
+    "node_modules/widest-line/node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "license": "MIT",
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/widest-line/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wildcard": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
+      "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi-cjs": {
+      "name": "wrap-ansi",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/write-file-atomic": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+      "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+      "license": "ISC",
+      "dependencies": {
+        "imurmurhash": "^0.1.4",
+        "is-typedarray": "^1.0.0",
+        "signal-exit": "^3.0.2",
+        "typedarray-to-buffer": "^3.1.5"
+      }
+    },
+    "node_modules/write-file-atomic/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==",
+      "license": "ISC"
+    },
+    "node_modules/ws": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/wsl-utils": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
+      "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-wsl": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/xdg-basedir": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz",
+      "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/xml-name-validator": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/xmlhttprequest-ssl": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
+      "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+      "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
+      "license": "ISC"
+    },
+    "node_modules/yaml": {
+      "version": "2.8.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+      "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "yaml": "bin.mjs"
+      },
+      "engines": {
+        "node": ">= 14.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/eemeli"
+      }
+    },
+    "node_modules/yaml-eslint-parser": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz",
+      "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.0.0",
+        "yaml": "^2.0.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ota-meshi"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/yoctocolors-cjs": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
+      "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zip-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz",
+      "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "archiver-utils": "^5.0.0",
+        "compress-commons": "^6.0.2",
+        "readable-stream": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    }
+  }
+}

+ 55 - 0
package.json

@@ -0,0 +1,55 @@
+{
+  "name": "quasar-skeleton",
+  "version": "0.0.1",
+  "description": "A skeleton for future projects",
+  "productName": "Quasar App",
+  "author": "Denis <denis.gnl@gmail.com>",
+  "type": "module",
+  "private": true,
+  "scripts": {
+    "lint": "eslint -c ./eslint.config.js \"./src*/**/*.{js,cjs,mjs,vue}\"",
+    "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
+    "test": "echo \"No test specified\" && exit 0",
+    "dev": "quasar dev",
+    "build": "quasar build"
+  },
+  "dependencies": {
+    "@bufbuild/protobuf": "^2.5.1",
+    "@quasar/cli": "^2.5.0",
+    "@quasar/extras": "^1.17.0",
+    "axios": "^1.13.2",
+    "chart.js": "^4.5.1",
+    "chartjs-plugin-datalabels": "^2.2.0",
+    "date-fns": "^3.6.0",
+    "fast-deep-equal": "^3.1.3",
+    "pinia": "^3.0.2",
+    "quasar": "^2.18.6",
+    "socket.io-client": "^4.8.1",
+    "vue": "^3.5.13",
+    "vue-chartjs": "^5.3.3",
+    "vue-currency-input": "^3.2.1",
+    "vue-i18n": "^11.1.4",
+    "vue-router": "^4.6.4"
+  },
+  "devDependencies": {
+    "@bufbuild/buf": "^1.61.0",
+    "@bufbuild/protoc-gen-es": "^2.10.2",
+    "@intlify/eslint-plugin-vue-i18n": "^4.0.1",
+    "@intlify/unplugin-vue-i18n": "^6.0.8",
+    "@eslint/js": "^9.27.0",
+    "@quasar/app-vite": "^2.4.0",
+    "@vue/eslint-config-prettier": "^10.2.0",
+    "autoprefixer": "^10.4.21",
+    "eslint": "^9.31.0",
+    "eslint-config-prettier": "^10.1.5",
+    "eslint-plugin-vue": "^10.1.0",
+    "postcss": "^8.5.3",
+    "prettier": "^3.5.3",
+    "vite-plugin-checker": "^0.9.3"
+  },
+  "engines": {
+    "node": "^24 || ^22 || ^20 || ^18",
+    "npm": ">= 6.13.4",
+    "yarn": ">= 1.21.1"
+  }
+}

+ 27 - 0
postcss.config.js

@@ -0,0 +1,27 @@
+import autoprefixer from 'autoprefixer'
+// import rtlcss from 'postcss-rtlcss'
+
+export default {
+  plugins: [
+    // https://github.com/postcss/autoprefixer
+    autoprefixer({
+      overrideBrowserslist: [
+        'last 4 Chrome versions',
+        'last 4 Firefox versions',
+        'last 4 Edge versions',
+        'last 4 Safari versions',
+        'last 4 Android versions',
+        'last 4 ChromeAndroid versions',
+        'last 4 FirefoxAndroid versions',
+        'last 4 iOS versions'
+      ]
+    }),
+
+    // https://github.com/elchininet/postcss-rtlcss
+    // If you want to support RTL css, then
+    // 1. yarn/pnpm/bun/npm install postcss-rtlcss
+    // 2. optionally set quasar.config.js > framework > lang to an RTL language
+    // 3. uncomment the following line (and its import statement above):
+    // rtlcss()
+  ]
+}

BIN
public/favicon.ico


BIN
public/icons/favicon-128x128.png


BIN
public/icons/favicon-16x16.png


BIN
public/icons/favicon-32x32.png


BIN
public/icons/favicon-96x96.png


+ 236 - 0
quasar.config.js

@@ -0,0 +1,236 @@
+/* eslint-env node */
+
+// Configuration for your app
+// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
+
+import { defineConfig } from "#q-app/wrappers";
+import { fileURLToPath } from "node:url";
+
+export default defineConfig((ctx) => {
+  return {
+    // https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
+    // preFetch: true,
+
+    // app boot file (/src/boot)
+    // --> boot files are part of "main.js"
+    // https://v2.quasar.dev/quasar-cli-vite/boot-files
+    boot: [
+      "axios",
+      "i18n",
+      "defaultPropsComponents",
+      // "socket.io",
+    ],
+
+    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
+    css: ["app.scss"],
+
+    // https://github.com/quasarframework/quasar/tree/dev/extras
+    extras: [
+      // 'ionicons-v4',
+      "mdi-v7",
+      // 'fontawesome-v6',
+      // 'eva-icons',
+      // 'themify',
+      // "line-awesome",
+      // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
+
+      "roboto-font", // optional, you are not bound to it
+      "material-icons", // optional, you are not bound to it
+    ],
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
+    build: {
+      target: {
+        browser: ["es2022", "firefox115", "chrome115", "safari14"],
+        node: "node22",
+      },
+
+      vueRouterMode: "history", // available values: 'hash', 'history'
+      // vueRouterBase,
+      // vueDevtools,
+      // vueOptionsAPI: false,
+
+      // rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
+
+      // publicPath: '/',
+      // analyze: true,
+      env: {
+        API_URL: ctx.dev ? "http://localhost:8000" : "http://localhost:8000",
+        PASSWORD: ctx.dev ? "S@ft2080." : "",
+        WEBSOCKET_API: ctx.dev
+          ? "http://localhost:4321/"
+          : "http://localhost:4321/",
+        WEBSOCKET_PATH: ctx.dev ? "/socket.io" : "/socket.io",
+        WEBSOCKET_ROOM: ctx.dev ? "LARAVEL" : "LARAVEL",
+        WEBSOCKET_API_KEY:
+          "7wArC/kl0nTbt4zBu0agw.NXLyjA96I6x1XmBcuokwPqfo3/CIxzqYw.PTthh5eqa08Uf4ubFlOqatpShoz1CRRID9pZReEFvBk3il6E9u",
+      },
+      // rawDefine: {}
+      // ignorePublicFolder: true,
+      // minify: false,
+      // polyfillModulePreload: true,
+      // distDir
+
+      // extendViteConf (viteConf) {},
+      // viteVuePluginOptions: {},
+
+      vitePlugins: [
+        [
+          "@intlify/unplugin-vue-i18n/vite",
+          {
+            include: [fileURLToPath(new URL("./src/i18n", import.meta.url))],
+            ssr: ctx.modeName === "ssr",
+          },
+        ],
+        [
+          "vite-plugin-checker",
+          {
+            eslint: {
+              lintCommand:
+                'eslint -c ./eslint.config.js "./src*/**/*.{js,mjs,cjs,vue}"',
+              useFlatConfig: true,
+            },
+          },
+          { server: false },
+        ],
+      ],
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
+    devServer: {
+      // https: true
+      open: true, // opens browser window automatically
+    },
+
+    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
+    framework: {
+      lang: "pt-BR",
+      config: {
+        dark: "auto",
+        notify: {
+          position: "top-right",
+        },
+      },
+
+      // iconSet: 'material-icons', // Quasar icon set
+      // lang: 'en-US', // Quasar language pack
+
+      // For special cases outside of where the auto-import strategy can have an impact
+      // (like functional components as one of the examples),
+      // you can manually specify Quasar components/directives to be available everywhere:
+      //
+      // components: [],
+      // directives: [],
+
+      // Quasar plugins
+      plugins: ["Dialog", "Notify", "Loading", "Cookies", "Dark"],
+    },
+
+    // animations: 'all', // --- includes all animations
+    // https://v2.quasar.dev/options/animations
+    animations: [],
+
+    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#property-sourcefiles
+    // sourceFiles: {
+    //   rootComponent: 'src/App.vue',
+    //   router: 'src/router/index',
+    //   store: 'src/store/index',
+    //   pwaRegisterServiceWorker: 'src-pwa/register-service-worker',
+    //   pwaServiceWorker: 'src-pwa/custom-service-worker',
+    //   pwaManifestFile: 'src-pwa/manifest.json',
+    //   electronMain: 'src-electron/electron-main',
+    //   electronPreload: 'src-electron/electron-preload'
+    //   bexManifestFile: 'src-bex/manifest.json
+    // },
+
+    // https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
+    ssr: {
+      prodPort: 3000, // The default port that the production server should use
+      // (gets superseded if process.env.PORT is specified at runtime)
+
+      middlewares: [
+        "render", // keep this as last one
+      ],
+
+      // extendPackageJson (json) {},
+      // extendSSRWebserverConf (esbuildConf) {},
+
+      // manualStoreSerialization: true,
+      // manualStoreSsrContextInjection: true,
+      // manualStoreHydration: true,
+      // manualPostHydrationTrigger: true,
+
+      pwa: false,
+
+      // pwaOfflineHtmlFilename: 'offline.html', // do NOT use index.html as name!
+      // will mess up SSR
+
+      // pwaExtendGenerateSWOptions (cfg) {},
+      // pwaExtendInjectManifestOptions (cfg) {}
+    },
+
+    // https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
+    pwa: {
+      workboxMode: "GenerateSW", // 'GenerateSW' or 'InjectManifest'
+      // swFilename: 'sw.js',
+      // manifestFilename: 'manifest.json'
+      // extendManifestJson (json) {},
+      // useCredentialsForManifestTag: true,
+      // injectPwaMetaTags: false,
+      // extendPWACustomSWConf (esbuildConf) {},
+      // extendGenerateSWOptions (cfg) {},
+      // extendInjectManifestOptions (cfg) {}
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova
+    cordova: {
+      // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor
+    capacitor: {
+      hideSplashscreen: true,
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron
+    electron: {
+      // extendElectronMainConf (esbuildConf) {},
+      // extendElectronPreloadConf (esbuildConf) {},
+
+      // extendPackageJson (json) {},
+
+      // Electron preload scripts (if any) from /src-electron, WITHOUT file extension
+      preloadScripts: ["electron-preload"],
+
+      // specify the debugging port to use for the Electron app when running in development mode
+      inspectPort: 5858,
+
+      bundler: "packager", // 'packager' or 'builder'
+
+      packager: {
+        // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
+        // OS X / Mac App Store
+        // appBundleId: '',
+        // appCategoryType: '',
+        // osxSign: '',
+        // protocol: 'myapp://path',
+        // Windows only
+        // win32metadata: { ... }
+      },
+
+      builder: {
+        // https://www.electron.build/configuration/configuration
+
+        appId: "quasar-skeleton",
+      },
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
+    bex: {
+      // extendBexScriptsConf (esbuildConf) {},
+      // extendBexManifestJson (json) {},
+
+      contentScripts: ["my-content-script"],
+    },
+  };
+});

+ 43 - 0
src/App.vue

@@ -0,0 +1,43 @@
+<template>
+  <router-view />
+</template>
+
+<script setup>
+import { Cookies, useQuasar } from "quasar";
+import { watch } from "vue";
+import { useI18n } from "vue-i18n";
+
+defineOptions({
+  name: "App",
+});
+
+const { locale } = useI18n();
+
+const $q = useQuasar();
+const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
+  ? "dark"
+  : "light";
+
+const theme = $q.cookies.get("theme") || systemTheme;
+const localeCookie = Cookies.get("locale") || window.navigator.language;
+console.log(theme, localeCookie);
+$q.dark.set(theme == "dark");
+
+watch(
+  () => $q.dark.isActive,
+  (value) => {
+    $q.cookies.set("theme", value ? "dark" : "light", {
+      expires: 365,
+    });
+  },
+);
+
+watch(
+  () => locale.value,
+  (value) => {
+    Cookies.set("locale", value, {
+      expires: 365,
+    });
+  },
+);
+</script>

+ 183 - 0
src/api/cacheService.js

@@ -0,0 +1,183 @@
+import api from "src/api";
+
+const createDbConnection = (dbName = "apiCache", version = 1) => {
+  let db = null;
+  const ready = new Promise((resolve, reject) => {
+    const request = indexedDB.open(dbName, version);
+
+    request.onerror = () => reject(request.error);
+    request.onsuccess = () => {
+      db = request.result;
+      resolve(db);
+    };
+
+    request.onupgradeneeded = (event) => {
+      const db = event.target.result;
+      if (!db.objectStoreNames.contains("apiCache")) {
+        db.createObjectStore("apiCache", { keyPath: "key" });
+      }
+    };
+  });
+
+  return {
+    getDb: () => ready,
+  };
+};
+
+const { getDb } = createDbConnection();
+
+const getFromCache = async (key) => {
+  const db = await getDb();
+
+  return new Promise((resolve) => {
+    const transaction = db.transaction(["apiCache"], "readonly");
+    const store = transaction.objectStore("apiCache");
+    const request = store.get(key);
+
+    request.onsuccess = () => {
+      const entry = request.result;
+      if (entry?.timestamp + entry?.ttl > Date.now()) {
+        resolve(entry.data);
+      } else {
+        resolve(null);
+      }
+    };
+  });
+};
+
+const setInCache = async (key, data, ttl) => {
+  const db = await getDb();
+  const entry = {
+    key,
+    data,
+    timestamp: Date.now(),
+    ttl: ttl * 1000,
+  };
+
+  return new Promise((resolve) => {
+    const transaction = db.transaction(["apiCache"], "readwrite");
+    const store = transaction.objectStore("apiCache");
+    store.put(entry);
+    transaction.oncomplete = () => resolve();
+  });
+};
+
+const clearCache = async (cacheKey) => {
+  const db = await getDb();
+  const transaction = db.transaction(["apiCache"], "readwrite");
+  const store = transaction.objectStore("apiCache");
+
+  return new Promise((resolve) => {
+    if (!cacheKey) {
+      store.clear();
+      transaction.oncomplete = () => resolve();
+      return;
+    }
+
+    const request = store.getAllKeys();
+    request.onsuccess = () => {
+      const deletePromises = request.result
+        .filter((key) => key.includes(cacheKey))
+        .map(
+          (key) =>
+            new Promise((resolve) => {
+              const deleteRequest = store.delete(key);
+              deleteRequest.onsuccess = () => resolve();
+            })
+        );
+
+      Promise.all(deletePromises).then(() => resolve());
+    };
+  });
+};
+
+const isCacheableValue = (value) => {
+  if (value === null) {
+    return false;
+  }
+
+  if (
+    typeof value === "object" &&
+    Object.keys(value).length === 1 &&
+    Object.prototype.hasOwnProperty.call(value, "data")
+  ) {
+    return false;
+  }
+
+  if (
+    typeof value === "object" &&
+    !Array.isArray(value) &&
+    Object.keys(value).length === 0
+  ) {
+    return false;
+  }
+
+  if (Array.isArray(value?.payload) && value?.payload?.length === 0) {
+    return false;
+  }
+
+  if (value?.payload == null) {
+    return false;
+  }
+
+  return true;
+};
+
+export const createCachedApi = (namespace, ttl = 3600) => {
+  const getCacheKey = (path) => `${namespace}:${path}`;
+
+  const getResourcePaths = (path) => {
+    const segments = path.split("/").filter(Boolean);
+    const paths = [];
+
+    for (let i = 0; i <= segments.length; i++) {
+      const currentPath = "/" + segments.slice(0, i).join("/");
+      paths.push(currentPath);
+    }
+
+    return paths;
+  };
+
+  const invalidateAll = async (path) => {
+    const paths = getResourcePaths(path);
+    await Promise.all(paths.map((path) => clearCache(getCacheKey(path))));
+  };
+
+  const get = async (path, options = {}) => {
+    const key = getCacheKey(path);
+    const cached = await getFromCache(key);
+
+    if (isCacheableValue(cached)) {
+      return { data: cached };
+    }
+
+    try {
+      const { data } = await api.get(path);
+      await setInCache(key, data, options.ttl ?? ttl);
+      return { data };
+    } catch (error) {
+      console.error(`API request for ${key} failed.`, error);
+      throw error;
+    }
+  };
+
+  const post = async (path, payload) => {
+    const { data } = await api.post(path, payload);
+    await invalidateAll(path);
+    return { data };
+  };
+
+  const put = async (path, payload) => {
+    const { data } = await api.put(path, payload);
+    await invalidateAll(path);
+    return { data };
+  };
+
+  const del = async (path) => {
+    const { data } = await api.delete(path);
+    await invalidateAll(path);
+    return { data };
+  };
+
+  return { get, post, put, del, invalidateAll };
+};

+ 38 - 0
src/api/city.js

@@ -0,0 +1,38 @@
+import { createCachedApi } from "./cacheService";
+
+const api = createCachedApi("city");
+
+export const getCity = async (id) => {
+  const { data } = await api.get("/city/" + id);
+  return data.payload;
+};
+
+export const getCities = async () => {
+  const { data } = await api.get("/city");
+  return data.payload;
+};
+
+export const getCityByState = async (stateId) => {
+  const { data } = await api.get(`/city-state/${stateId}`);
+  return data.payload;
+};
+
+export const getCityByCountry = async (countryId) => {
+  const { data } = await api.get(`/city-country/${countryId}`);
+  return data.payload;
+};
+
+export const createCity = async (city) => {
+  const { data } = await api.post("/city", city);
+  return data.payload;
+};
+
+export const updateCity = async (city, id) => {
+  const { data } = await api.put(`/city/${id}`, city);
+  return data.payload;
+};
+
+export const deleteCity = async (id) => {
+  const { data } = await api.del(`/city/${id}`);
+  return data.payload;
+};

+ 28 - 0
src/api/country.js

@@ -0,0 +1,28 @@
+import { createCachedApi } from "./cacheService";
+
+const api = createCachedApi("country");
+
+export const getCountry = async (id) => {
+  const { data } = await api.get("/country/" + id);
+  return data.payload;
+};
+
+export const getCountries = async () => {
+  const { data } = await api.get("/country");
+  return data.payload;
+};
+
+export const createCountry = async (country) => {
+  const data = await api.post("/country", country);
+  return data.payload;
+};
+
+export const updateCountry = async (country, id) => {
+  const data = await api.put(`/country/${id}`, country);
+  return data.payload;
+};
+
+export const deleteCountry = async (id) => {
+  const data = await api.del(`/country/${id}`);
+  return data.payload;
+};

+ 2 - 0
src/api/index.js

@@ -0,0 +1,2 @@
+import { api } from "boot/axios";
+export default api;

+ 11 - 0
src/api/permission.js

@@ -0,0 +1,11 @@
+import api from "src/api";
+
+export const getGuestPermissions = async () => {
+  const { data } = await api.get("/permissions-by-type/guest");
+  return data.payload;
+};
+
+export const getUserPermissions = async () => {
+  const { data } = await api.get("/permissions-by-type");
+  return data.payload;
+};

+ 33 - 0
src/api/state.js

@@ -0,0 +1,33 @@
+import { createCachedApi } from "./cacheService";
+
+const api = createCachedApi("state");
+
+export const getState = async (id) => {
+  const { data } = await api.get("/state/" + id);
+  return data.payload;
+}
+
+export const getStates = async () => {
+  const { data } = await api.get("/state");
+  return data.payload;
+}
+
+export const getStateByCountry = async (countryId) => {
+  const { data } = await api.get(`/state-country/${countryId}`);
+  return data.payload;
+}
+
+export const createState = async (state) => {
+  const { data } = await api.post("/state", state);
+  return data.payload;
+}
+
+export const updateState = async (state, id) => {
+  const { data } = await api.put(`/state/${id}`, state);
+  return data.payload;
+}
+
+export const deleteState = async (id) => {
+  const { data } = await api.del(`/state/${id}`);
+  return data.payload;
+}

+ 31 - 0
src/api/user.js

@@ -0,0 +1,31 @@
+import api from "src/api";
+
+export const getUser = async () => {
+  const { data } = await api.get("/user/me");
+  return data.payload;
+};
+
+export const getUsers = async () => {
+  const { data } = await api.get("/user");
+  return data.payload;
+};
+
+export const createUser = async (user) => {
+  const { data } = await api.post("/user", user);
+  return data.payload;
+};
+
+export const updateUser = async (user, id) => {
+  const { data } = await api.put(`/user/${id}`, user);
+  return data.payload;
+};
+
+export const deleteUser = async (id) => {
+  const { data } = await api.delete(`/user/${id}`);
+  return data.payload;
+};
+
+export const userTypes = async () => {
+  const { data } = await api.get("/user-types");
+  return data.payload;
+};

BIN
src/assets/logo.png


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
src/assets/softpar_logo_dark.svg


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
src/assets/softpar_logo_light.svg


+ 1 - 0
src/assets/softpar_logo_mini.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-15 -15 80 80" fill="none"><rect width="50" height="50" rx="25" fill="#21316C"></rect><path d="M10 19.549C10 14.8784 13.1257 12 17.474 12C21.8222 12 25.0591 15.0456 25.0591 19.5768C25.0591 24.4191 21.4518 26.8101 18.1547 26.8101C16.307 26.8101 14.7743 26.0672 13.8342 24.6466V32.4509H10V19.549ZM21.1646 19.405C21.1646 17.1533 19.6874 15.5888 17.5018 15.5888C15.3161 15.5888 13.8389 17.1533 13.8389 19.405C13.8389 21.6567 15.3161 23.2213 17.5018 23.2213C19.6874 23.2213 21.1646 21.6521 21.1646 19.405Z" fill="#EEF4FF"></path><path d="M25.5635 19.5768C25.5635 15.0456 28.6892 12 33.1486 12C37.608 12 40.6226 14.962 40.6226 19.549V26.4433H37.0986V23.9084C36.3021 25.8165 34.5702 26.8147 32.4401 26.8147C29.2032 26.8147 25.5635 24.4237 25.5635 19.5815V19.5768ZM36.7883 19.405C36.7883 17.1533 35.3111 15.5888 33.1208 15.5888C30.9305 15.5888 29.4579 17.1533 29.4579 19.405C29.4579 21.6567 30.9351 23.2213 33.1208 23.2213C35.3065 23.2213 36.7883 21.6521 36.7883 19.405Z" fill="#EEF4FF"></path><path d="M26.0549 38.8485C19.9933 38.8485 16.1359 35.139 15.0801 33.9737L17.8168 31.4713C18.3957 32.1073 21.5353 35.2736 26.4022 35.1297C30.63 34.9997 34.4179 31.9541 35.1311 31.2067L37.8122 33.7694C37.7659 33.8205 32.9963 38.6442 26.5179 38.8438C26.3651 38.8485 26.2077 38.8531 26.0595 38.8531L26.0549 38.8485Z" fill="#0F86FA"></path></svg>

+ 0 - 0
src/boot/.gitkeep


+ 126 - 0
src/boot/axios.js

@@ -0,0 +1,126 @@
+import { defineBoot } from "#q-app/wrappers";
+import { Cookies, Notify } from "quasar";
+import axios from "axios";
+import { useRouter } from "vue-router";
+import { userStore } from "src/stores/user";
+
+const api = axios.create({
+  baseURL: process.env.API_URL + "/api",
+  withCredentials: true,
+});
+
+api.interceptors.request.use(
+  async (config) => {
+    const accessToken = userStore().accessToken;
+    const savedLanguage = Cookies.get("locale");
+    const language = savedLanguage || window.navigator.language;
+    config.headers["Accept-Language"] = language;
+    if (accessToken) {
+      config.headers.Authorization = `Bearer ${accessToken}`;
+    }
+    return config;
+  },
+  (error) => {
+    return Promise.reject(error);
+  },
+);
+
+let isRefreshing = false;
+let failedQueue = [];
+
+const processQueue = (error, token = null) => {
+  failedQueue.forEach((prom) => {
+    if (error) {
+      prom.reject(error);
+    } else {
+      prom.config.headers["Authorization"] = "Bearer " + token;
+      prom.resolve(api(prom.config));
+    }
+  });
+  failedQueue = [];
+};
+
+const errorInterceptor = async (error, router) => {
+  const originalRequest = error.config;
+  const user_store = userStore();
+
+  if (error.response?.status === 422) {
+    return Promise.reject(error);
+  }
+
+  if (
+    error.response?.status !== 401 ||
+    originalRequest.url.includes("/refresh")
+  ) {
+    if (error?.response?.data?.message) {
+      Notify.create({
+        message: error?.response?.data?.message,
+        type: "negative",
+      });
+    }
+    return Promise.reject(error);
+  }
+
+  if (error?.config?.url === "/login") {
+    if (error?.response?.data?.message) {
+      Notify.create({
+        message: error?.response?.data?.message,
+        type: "negative",
+      });
+    }
+    return Promise.reject(error);
+  }
+
+  if (isRefreshing) {
+    return new Promise((resolve, reject) => {
+      failedQueue.push({ resolve, reject, config: originalRequest });
+    });
+  }
+
+  isRefreshing = true;
+  try {
+    const response = await api.post("/refresh");
+    const newAccessToken = response.data.payload.access_token;
+    user_store.accessToken = newAccessToken;
+    originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;
+    processQueue(null, newAccessToken);
+    return api(originalRequest);
+  } catch (err) {
+    processQueue(err, null);
+    user_store.resetUser();
+    router.push("/login");
+    return Promise.reject(err);
+  } finally {
+    isRefreshing = false;
+  }
+};
+
+const successInterceptor = (response) => {
+  if (response?.data?.message) {
+    Notify.create({
+      message: response?.data?.message,
+      type: "positive",
+    });
+  }
+  return response;
+};
+
+export default defineBoot(({ app }) => {
+  const router = useRouter();
+
+  api.interceptors.response.use(
+    (response) => successInterceptor(response),
+    (error) => errorInterceptor(error, router),
+  );
+
+  // for use inside Vue files (Options API) through this.$axios and this.$api
+  app.config.globalProperties.$axios = axios;
+  // ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
+  //       so you won't necessarily have to import axios in each vue file
+
+  app.config.globalProperties.$api = api;
+  // ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
+  //       so you can easily perform requests against your app's API
+});
+
+export { api };

+ 42 - 0
src/boot/defaultPropsComponents.js

@@ -0,0 +1,42 @@
+import { QDialog, QInput, QSelect, QScrollArea, QCard } from "quasar";
+import { defineBoot } from "#q-app/wrappers";
+
+/**
+ * Set some default properties on a component
+ */
+const SetComponentDefaults = (component, defaults) => {
+  Object.keys(defaults).forEach((prop) => {
+    component.props[prop] =
+      Array.isArray(component.props[prop]) === true ||
+      typeof component.props[prop] === "function"
+        ? { type: component.props[prop], default: defaults[prop] }
+        : { ...component.props[prop], default: defaults[prop] };
+  });
+};
+
+export default defineBoot(() => {
+  SetComponentDefaults(QDialog, {
+    transitionShow: "slide-up",
+    transitionHide: "slide-down",
+  });
+  SetComponentDefaults(QInput, {
+    filled: true,
+  });
+  SetComponentDefaults(QSelect, {
+    filled: true,
+    behavior: "menu",
+  });
+  SetComponentDefaults(QCard, {
+    flat: true,
+  });
+  SetComponentDefaults(QScrollArea, {
+    thumbStyle: {
+      borderRadius: "4px",
+      background: "#A6A6A6",
+      width: "6px",
+      opacity: 0.6,
+    },
+    verticalOffset: [4, 2],
+    horizontalOffset: [0, 2],
+  });
+});

+ 18 - 0
src/boot/i18n.js

@@ -0,0 +1,18 @@
+import { defineBoot } from "#q-app/wrappers";
+import { createI18n } from "vue-i18n";
+import { Cookies } from "quasar";
+import messages from "src/i18n";
+
+const i18n = createI18n({
+  locale: Cookies.get("locale")
+    ? Cookies.get("locale")
+    : window.navigator.language,
+  globalInjection: true,
+  messages,
+});
+
+export default defineBoot(({ app }) => {
+  app.use(i18n);
+});
+
+export { i18n };

+ 158 - 0
src/boot/socket.io.js

@@ -0,0 +1,158 @@
+import { defineBoot } from "#q-app/wrappers";
+import { io } from "socket.io-client";
+import { reactive } from "vue";
+
+const state = reactive({
+  activeRooms: new Set(),
+  isConnected: false,
+});
+
+const socket = io(process.env.WEBSOCKET_API, {
+  transport: ["websocket"],
+  path: process.env.WEBSOCKET_PATH,
+  auth: {
+    apiKey: process.env.WEBSOCKET_API_KEY,
+  },
+  reconnection: true,
+  reconnectionDelay: 1000,
+  timeout: 20000,
+});
+
+/**
+ * Join a room with automatic reconnection handling
+ * @param {string} roomName Room name to join
+ * @return {boolean} Success status
+ */
+const joinRoom = (roomName) => {
+  if (!roomName) return false;
+
+  const fullRoomName = `${process.env.WEBSOCKET_ROOM}:${roomName}`;
+
+  state.activeRooms.add(fullRoomName);
+
+  if (state.isConnected) {
+    socket.emit("join", fullRoomName);
+    console.log(`Joined room: ${fullRoomName}`);
+  } else {
+    console.log(`Room ${fullRoomName} will join upon connection`);
+  }
+
+  return true;
+};
+
+/**
+ * Leave a room and clean up
+ * @param {string} roomName Room name to leave
+ */
+const leaveRoom = (roomName) => {
+  if (!roomName) return;
+
+  const fullRoomName = `${process.env.WEBSOCKET_ROOM}:${roomName}`;
+
+  state.activeRooms.delete(fullRoomName);
+
+  if (state.isConnected) {
+    socket.emit("leave", fullRoomName);
+    console.log(`Left room: ${fullRoomName}`);
+  }
+};
+
+const sendEventToLaravel = (eventName, data) => {
+  const channel = process.env.WEBSOCKET_ROOM + ":" + eventName;
+  socket.emit("eventWrapperToLaravel", {
+    channel: channel,
+    data: data,
+  });
+};
+
+const sendEvent = (room, eventName, data) => {
+  const channel = process.env.WEBSOCKET_ROOM + ":" + room + "@" + eventName;
+  socket.emit("eventWrapperToNode", {
+    channel: channel,
+    data: data,
+  });
+};
+
+export default defineBoot(async () => {
+  socket.on("connect", () => {
+    console.log("Connected to websocket server!");
+    state.isConnected = true;
+
+    // Rejoin all active rooms after reconnection
+    if (state.activeRooms.size > 0) {
+      console.log(
+        `Rejoining ${state.activeRooms.size} rooms after reconnection`,
+      );
+      state.activeRooms.forEach((room) => {
+        socket.emit("join", room);
+      });
+    }
+  });
+
+  socket.on("disconnect", () => {
+    console.log("Disconnected from websocket server!");
+    state.isConnected = false;
+  });
+
+  socket.on("connect_error", (error) => {
+    console.error("Websocket connection error: ", error);
+    state.isConnected = false;
+  });
+
+  socket.on("connect_timeout", (timeout) => {
+    console.error("Websocket connection timeout: ", timeout);
+    state.isConnected = false;
+  });
+
+  socket.on("reconnect", (attemptNumber) => {
+    console.log(
+      "Reconnected to websocket server! Attempt number: ",
+      attemptNumber,
+    );
+    state.isConnected = true;
+  });
+
+  socket.on("reconnect_attempt", (attemptNumber) => {
+    console.log("Reconnect attempt number: ", attemptNumber);
+  });
+});
+
+/**
+ * Helper function to create one-time event listeners with automatic cleanup
+ * @param {string} event Event name
+ * @param {Function} callback Callback function
+ * @param {string|null} roomName Optional room to leave on callback
+ * @return {Function} Cleanup function
+ */
+const onceEvent = (event, callback, roomName = null) => {
+  if (!event || typeof callback !== "function") return () => {};
+
+  const wrappedCallback = (data) => {
+    socket.off(event, wrappedCallback);
+
+    if (roomName) {
+      leaveRoom(roomName);
+    }
+
+    callback(data);
+  };
+
+  socket.on(event, wrappedCallback);
+
+  return () => {
+    socket.off(event, wrappedCallback);
+    if (roomName) {
+      leaveRoom(roomName);
+    }
+  };
+};
+
+export {
+  socket,
+  joinRoom,
+  leaveRoom,
+  sendEvent,
+  sendEventToLaravel,
+  onceEvent,
+  state,
+};

+ 77 - 0
src/components/charts/CardIconChart.vue

@@ -0,0 +1,77 @@
+<template>
+  <q-card
+    flat
+    class="full-height q-pa-lg"
+    :style="{
+      minHeight: $q.screen.lt.sm ? '400px' : '600px',
+      minWidth: $q.screen.lt.sm ? '200px' : '300px',
+    }"
+  >
+    <div class="column no-wrap full-width">
+      <div class="flex justify-between items-center no-wrap">
+        <span class="text-h5">{{ title }}</span>
+        <div class="flex no-wrap flex-center">
+          <div class="round background">
+            <q-icon class="q-pa-sm" :name="icon" size="24px" :color="color" />
+          </div>
+          <!-- <q-icon
+            v-if="downloadImage !== null"
+            name="mdi-dots-vertical"
+            size="sm"
+            style="width: 12px;margin-right: -12px"
+            @click="downloadImage"
+          /> -->
+        </div>
+      </div>
+    </div>
+    <div class="flex flex-grow flex-center" style="height: calc(100% - 30px)">
+      <template v-if="hasChartSlot">
+        <slot name="chart"></slot>
+      </template>
+      <template v-else>
+        <div v-if="!loading" class="q-my-md row justify-center full-width">
+          <div class="q-pa-md body2">
+            {{ $t("http.errors.no_records_found") }}
+          </div>
+        </div>
+      </template>
+    </div>
+  </q-card>
+</template>
+
+<script setup>
+import { useSlots } from "vue";
+
+const hasChartSlot = useSlots("chart");
+
+const { color, title, icon } = defineProps({
+  color: {
+    type: String,
+    default: "primary",
+  },
+  title: {
+    type: String,
+    default: "Usuários",
+  },
+  icon: {
+    type: String,
+    default: "mdi-account",
+  },
+});
+</script>
+<style lang="scss" scoped>
+@use "sass:map";
+@use "src/css/quasar.variables.scss";
+
+body.body--light {
+  .background {
+    background: rgba(map.get($colors, "primary"), 0.2) !important;
+  }
+}
+
+body.body--dark {
+  .background {
+    background: rgba(map.get($colors-dark, "primary"), 0.2) !important;
+  }
+}
+</style>

+ 81 - 0
src/components/charts/CardIconMiniChart.vue

@@ -0,0 +1,81 @@
+<template>
+  <q-card flat class="q-pa-lg" style="max-height: 184px; min-width: 305px;">
+    <div class="column no-wrap full-width">
+      <div class="flex justify-between items-center no-wrap">
+        <span class="text-h5">{{ title }}</span>
+        <div class="round background">
+          <q-icon class="q-pa-sm" :name="icon" size="24px" :color="color" />
+        </div>
+      </div>
+      <div class="flex no-wrap full-width justify-between q-pa-sm">
+        <div class="column flex-center">
+          <span :class="numberCard.length >= 7 ? 'text-h4' : 'text-h3'">
+            {{ numberCard }}
+          </span>
+          <div
+            v-if="numberPorcent !== null"
+            class="flex no-wrap text-subtitle2"
+            :class="numberPorcent > 0 ? 'text-positive' : 'text-negative'"
+          >
+            <q-icon
+              :name="numberPorcent > 0 ? 'mdi-arrow-up' : 'mdi-arrow-down'"
+              size="18px"
+              class="q-mr-xs"
+            />
+            {{ numberPorcent + "%" }}
+          </div>
+        </div>
+        <div class="flex justify-end" style="max-width: 120px; height: 80px">
+          <slot name="chart" :color="color" :chart-data="chartData" />
+        </div>
+      </div>
+    </div>
+  </q-card>
+</template>
+
+<script setup>
+const { color, title, icon, chartData, numberCard, numberPorcent } =
+  defineProps({
+    color: {
+      type: String,
+      default: "primary",
+    },
+    title: {
+      type: String,
+      default: "Usuários",
+    },
+    icon: {
+      type: String,
+      default: "mdi-account",
+    },
+    chartData: {
+      type: Array,
+      default: () =>
+        Array.from({ length: 7 }, () => Math.floor(Math.random() * 100)),
+    },
+    numberCard: {
+      type: String,
+      default: () => Math.floor(Math.random() * 100),
+    },
+    numberPorcent: {
+      type: Number,
+      default: () => Math.ceil(Math.random() * 200 - 100),
+    },
+  });
+</script>
+<style lang="scss" scoped>
+@use "sass:map";
+@use "src/css/quasar.variables.scss";
+
+body.body--light {
+  .background {
+    background: rgba(map.get($colors, "primary"), 0.2) !important;
+  }
+}
+
+body.body--dark {
+  .background {
+    background: rgba(map.get($colors-dark, "primary"), 0.2) !important;
+  }
+}
+</style>

+ 285 - 0
src/components/charts/custom/NPSChart.vue

@@ -0,0 +1,285 @@
+<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
+<template>
+  <q-card v-bind="$attrs" class="q-px-md">
+    <q-card-section class="row justify-between">
+      <div>
+        <div class="text-h6">{{ props.title }}</div>
+        <span>{{ props.subTitle }}</span>
+      </div>
+      <q-btn
+        icon="mdi-tray-arrow-down"
+        dense
+        flat
+        class="q-my-auto"
+        @click="downloadImage"
+      />
+    </q-card-section>
+    <q-separator dark inset />
+    <div class="graph-container">
+      <div class="column">
+        <div class="col-4 row">
+          <div
+            class="bg-nps-green"
+            style="width: 1rem; min-height: max-content"
+          />
+          <div class="column q-pa-md">
+            <span class="text-bold text-h6 text-nps-green">
+              {{ $t("charts.nps.promotion_zone") }}
+            </span>
+            <span>{{ $t("charts.nps.promotion_zone_range") }}</span>
+          </div>
+        </div>
+        <div class="col-4 row">
+          <div
+            class="bg-nps-green-light"
+            style="width: 1rem; min-height: max-content"
+          />
+          <div class="column q-pa-md">
+            <span class="text-bold text-h6 text-nps-green-light">
+              {{ $t("charts.nps.quality_zone") }}
+            </span>
+            <span>{{ $t("charts.nps.quality_zone_range") }}</span>
+          </div>
+        </div>
+        <div class="col-4 row">
+          <div
+            class="bg-nps-yellow"
+            style="width: 1rem; min-height: max-content"
+          />
+          <div class="column q-pa-md">
+            <span class="text-bold text-h6 text-nps-yellow">
+              {{ $t("charts.nps.refinement_zone") }}
+            </span>
+            <span>{{ $t("charts.nps.refinement_zone_range") }}</span>
+          </div>
+        </div>
+        <div class="col-4 row">
+          <div
+            class="bg-nps-red"
+            style="width: 1rem; min-height: max-content"
+          />
+          <div class="column q-pa-md">
+            <span class="text-bold text-h6 text-nps-red">
+              {{ $t("charts.nps.critical_zone") }}</span
+            >
+            <span>{{ $t("charts.nps.critical_zone_range") }}</span>
+          </div>
+        </div>
+      </div>
+
+      <div style="display: flex; flex-direction: column; align-items: center">
+        <div style="max-height: 500px">
+          <Doughnut
+            ref="chart_ref"
+            :options="chartOptions"
+            :data="chartData"
+            :plugins="[ChartDataLabels, gaugeNeedle]"
+          />
+        </div>
+        <span>{{ titulo }}</span>
+      </div>
+
+      <div class="column q-col-gutter-md">
+        <div class="column">
+          <span>
+            {{ $t("charts.nps.promoters") }} -
+            {{ ((data.promotores / data.total) * 100).toFixed(0) }} %
+          </span>
+          <q-linear-progress
+            :value="data.promotores / data.total"
+            rounded
+            size="20px"
+            color="nps-green"
+            style="min-width: 200px"
+          />
+        </div>
+        <div class="column">
+          <span
+            >{{ $t("charts.nps.passives") }} -
+            {{ ((data.neutros / data.total) * 100).toFixed(0) }} %</span
+          >
+          <q-linear-progress
+            :value="data.neutros / data.total"
+            rounded
+            size="20px"
+            color="nps-yellow"
+            style="min-width: 200px"
+          />
+        </div>
+        <div class="column">
+          <span
+            >{{ $t("charts.nps.detractors") }} -
+            {{ ((data.detratores / data.total) * 100).toFixed(0) }} %</span
+          >
+          <q-linear-progress
+            :value="data.detratores / data.total"
+            rounded
+            size="20px"
+            color="nps-red"
+            style="min-width: 200px"
+          />
+        </div>
+      </div>
+    </div>
+  </q-card>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
+import { Doughnut } from "vue-chartjs";
+import ChartDataLabels from "chartjs-plugin-datalabels";
+import { base64ToJPEG } from "src/helpers/convertBase64Image";
+
+const props = defineProps({
+  title: {
+    type: String,
+    required: true,
+  },
+  value: {
+    type: Number,
+    required: true,
+  },
+  subTitle: {
+    type: String,
+    default: null,
+  },
+  data: {
+    type: Object,
+    required: true,
+  },
+});
+
+const chart_ref = ref(null);
+const titulo = ref(`Valor: ${props.data?.nps}`);
+
+const gaugeNeedle = {
+  id: "gaugeNeedle",
+  afterDatasetsDraw(chart) {
+    const { ctx, data } = chart;
+    ctx.save();
+
+    const needleValue = data.datasets[0].needleValue;
+    const xCenter = chart.getDatasetMeta(0).data[0].x;
+    const yCenter = chart.getDatasetMeta(0).data[0].y;
+    const outerRadius = chart.getDatasetMeta(0).data[0].outerRadius - 40;
+    const angle = Math.PI;
+
+    let circumference =
+      (chart.getDatasetMeta(0).data[0].circumference /
+        Math.PI /
+        data.datasets[0].data[0]) *
+      needleValue;
+
+    const needleAngleValue = circumference + 1.5;
+
+    ctx.translate(xCenter, yCenter);
+    ctx.rotate(angle * needleAngleValue);
+
+    // Draw the needle
+    ctx.beginPath();
+    ctx.strokeStyle = "grey";
+    ctx.fillStyle = "grey";
+    ctx.moveTo(0 - 4, 0);
+    ctx.lineTo(0, -outerRadius);
+    ctx.lineTo(0 + 4, 0);
+    ctx.stroke();
+    ctx.fill();
+
+    ctx.beginPath();
+    ctx.arc(0, 0, 8, 0, 2 * Math.PI);
+    ctx.fillStyle = "grey";
+    ctx.fill();
+
+    ctx.restore();
+  },
+};
+
+ChartJS.register(ArcElement, Tooltip, Legend);
+
+const chartData = ref({
+  datasets: [
+    {
+      backgroundColor: ["#d43333", "#ffbe00", "#40c56c", "#0d733e"],
+      data: [100, 50, 25, 25],
+      needleValue: Number(props.data?.nps) + 100,
+      borderColor: "transparent",
+    },
+  ],
+});
+
+const chartOptions = ref({
+  rotation: 270,
+  circumference: 180,
+  cutout: "50%",
+  plugins: {
+    tooltip: {
+      enabled: false,
+    },
+    datalabels: {
+      color: "white",
+      font: {
+        size: 14,
+        weight: "bold",
+      },
+      formatter: (value, ctx) => {
+        let valor = "";
+        switch (ctx.dataIndex) {
+          case 0:
+            valor = "-100 a 0";
+            break;
+          case 1:
+            valor = "0 a 50";
+            break;
+          case 2:
+            valor = "50 a 75";
+            break;
+          case 3:
+            valor = "75 a 100";
+            break;
+          default:
+            valor = "";
+            break;
+        }
+        return valor;
+      },
+    },
+  },
+});
+
+addEventListener("resize", () => {
+  if (chart_ref.value) {
+    chart_ref.value.chart.update();
+  }
+});
+
+const downloadImage = () => {
+  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
+  base64ToJPEG(image, props.title);
+};
+</script>
+
+<style scoped>
+.graph-container {
+  display: grid;
+  grid-template-columns: 1fr 1fr 1fr;
+  align-items: center;
+  justify-content: space-around;
+  gap: 1rem;
+  width: 100%;
+  max-width: 100vw;
+  padding: 1rem;
+}
+
+@media screen and (max-width: 1024px) {
+  .graph-container {
+    grid-template-columns: 1fr 1fr;
+  }
+}
+
+@media screen and (max-width: 768px) {
+  .graph-container {
+    grid-template-columns: 1fr;
+  }
+}
+</style>

+ 201 - 0
src/components/charts/custom/SpeedometerChart.vue

@@ -0,0 +1,201 @@
+<template>
+  <q-card v-bind="$attrs" class="q-px-md full-height column justify-between">
+    <q-card-section class="q-pa-none">
+      <div class="row justify-between no-wrap items-start q-py-md q-px-sm">
+        <div>
+          <div class="text-bold text-description q-mb-sm">
+            {{ String(props.title).toLocaleUpperCase() }}
+          </div>
+          <span>{{ props.subTitle }}</span>
+        </div>
+        <q-btn
+          icon="mdi-tray-arrow-down"
+          class="q-ml-md"
+          dense
+          flat
+          @click="downloadImage"
+        />
+      </div>
+      <q-separator dark />
+    </q-card-section>
+    <div class="graph-container">
+      <Doughnut
+        ref="chart_ref"
+        :options="chartOptions"
+        :data="chartData"
+        :plugins="[ChartDataLabels, gaugeNeedle]"
+      />
+    </div>
+  </q-card>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
+import { Doughnut } from "vue-chartjs";
+import ChartDataLabels from "chartjs-plugin-datalabels";
+import { base64ToJPEG } from "src/helpers/convertBase64Image";
+
+const props = defineProps({
+  title: {
+    type: String,
+    required: true,
+  },
+  valorAgulha: {
+    type: Number,
+    required: true,
+  },
+  subTitle: {
+    type: String,
+    default: null,
+  },
+});
+
+const chart_ref = ref(null);
+const titulo = ref(`Valor: ${props.valorAgulha}`);
+
+const gaugeNeedle = {
+  id: "gaugeNeedle",
+  afterDatasetsDraw(chart) {
+    const { ctx, data } = chart;
+    ctx.save();
+
+    const needleValue = data.datasets[0].needleValue;
+    const xCenter = chart.getDatasetMeta(0).data[0].x;
+    const yCenter = chart.getDatasetMeta(0).data[0].y;
+    const outerRadius = chart.getDatasetMeta(0).data[0].outerRadius - 40;
+    const angle = Math.PI;
+
+    let circumference =
+      (chart.getDatasetMeta(0).data[0].circumference /
+        Math.PI /
+        data.datasets[0].data[0]) *
+      needleValue;
+
+    const needleAngleValue = circumference + 1.5;
+
+    ctx.translate(xCenter, yCenter);
+    ctx.rotate(angle * needleAngleValue);
+
+    // Draw the needle
+    ctx.beginPath();
+    ctx.strokeStyle = "grey";
+    ctx.fillStyle = "grey";
+    ctx.moveTo(0 - 4, 0);
+    ctx.lineTo(0, -outerRadius);
+    ctx.lineTo(0 + 4, 0);
+    ctx.stroke();
+    ctx.fill();
+
+    ctx.beginPath();
+    ctx.arc(0, 0, 8, 0, 2 * Math.PI);
+    ctx.fillStyle = "grey";
+    ctx.fill();
+
+    ctx.restore();
+  },
+};
+
+ChartJS.register(ArcElement, Tooltip, Legend);
+
+const chartData = ref({
+  datasets: [
+    {
+      backgroundColor: [
+        "#00a550",
+        "#4dbb7e",
+        "#9ad2ad",
+        "#cce156",
+        "#fff100",
+        "#ffbe00",
+        "#ff8c00",
+        "#FC3D23",
+        "#D01616",
+        "#8A0000",
+      ],
+      data: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+      needleValue: props.valorAgulha,
+      borderColor: "transparent",
+    },
+  ],
+});
+
+const chartOptions = ref({
+  rotation: 270,
+  circumference: 180,
+  cutout: "50%",
+  plugins: {
+    tooltip: {
+      enabled: false,
+    },
+    title: {
+      display: true,
+      text: titulo.value,
+      color: "#ffffff",
+      position: "bottom",
+    },
+    datalabels: {
+      color: "black",
+      font: {
+        size: 14,
+        weight: "bold",
+      },
+      formatter: (value, ctx) => {
+        const valor = ctx.dataIndex;
+        return valor;
+      },
+    },
+  },
+});
+
+addEventListener("resize", () => {
+  if (chart_ref.value) {
+    chart_ref.value.chart.update();
+  }
+});
+
+const downloadImage = () => {
+  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
+  base64ToJPEG(image, props.title);
+};
+</script>
+
+<style scoped>
+.graph-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  max-height: 300px;
+}
+
+@media screen and (max-width: 1600px) {
+  .graph-container {
+    max-height: 250px;
+  }
+}
+
+@media screen and (max-width: 1360px) {
+  .graph-container {
+    max-height: 200px;
+  }
+}
+
+@media screen and (max-width: 1130px) {
+  .graph-container {
+    max-height: 175px;
+  }
+}
+
+@media screen and (max-width: 888px) {
+  .graph-container {
+    max-height: 250px;
+  }
+}
+
+@media screen and (max-width: 650px) {
+  .graph-container {
+    max-height: 300px;
+  }
+}
+</style>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 41 - 0
src/components/charts/maps/Brasil/DadosBrasil.js


+ 228 - 0
src/components/charts/maps/Brasil/MapaBrasil.vue

@@ -0,0 +1,228 @@
+<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
+<template>
+  <div>
+    <q-card style="height: 100%">
+      <q-card-section class="row justify-between">
+        <div class="column">
+          <span class="text-bold text-description q-mb-sm">
+            {{ props.title }}
+          </span>
+          <span>{{ props.subtitle }}</span>
+        </div>
+        <q-btn
+          icon="mdi-tray-arrow-down"
+          dense
+          flat
+          class="q-my-auto"
+          @click="downloadImage"
+        />
+      </q-card-section>
+
+      <q-separator inset />
+      <q-card-section>
+        <div style="display: flex; flex-direction: column">
+          <div @mouseover="hideInfoBox">
+            <svg
+              ref="ref_mapa"
+              xmlns="http://www.w3.org/2000/svg"
+              class="mapa-svg-estados"
+              viewBox="120 50 500 500"
+              @click="mapclick"
+            >
+              <g id="mapa-svg-area">
+                <MapaEstado
+                  v-for="item in items"
+                  ref="child"
+                  :key="item.uf"
+                  :item="item"
+                  :show-circle-info="showCircleInfo"
+                  :class="item.classObject"
+                  :style="item.color ? `fill: ${item.color}` : ''"
+                  @state-selected-event="onStateSelectedEvent"
+                  @state-mouse-over-event="onStateMouseOverEvent"
+                />
+              </g>
+            </svg>
+          </div>
+          <div class="bg-background--2 q-pa-md" style="border-radius: 0.5rem">
+            <div
+              v-if="selected"
+              style="display: flex; flex-direction: column; gap: 0.5rem"
+            >
+              <span>
+                <b> Estado:</b> {{ dadosEstado.name }} -
+                {{ dadosEstado.uf.toUpperCase() }}
+              </span>
+              <span><b>Participantes:</b> {{ dadosEstado.pessoas || 0 }}</span>
+            </div>
+            <div
+              v-else
+              style="display: flex; flex-direction: column; gap: 0.5rem"
+            >
+              <span>
+                <b>Selecione um estado</b>
+              </span>
+            </div>
+          </div>
+        </div>
+      </q-card-section>
+    </q-card>
+  </div>
+</template>
+
+<script setup>
+import { computed, onMounted, ref } from "vue";
+import MapaEstado from "../MapaEstado.vue";
+import { DadosBrasil } from "./DadosBrasil";
+import { base64ToJPEG } from "src/helpers/convertBase64Image";
+
+const selected = ref(null);
+const showCircleInfo = ref(false);
+const infoBoxActive = ref(false);
+const infoBoxPosX = ref(0);
+const infoBoxPosY = ref(0);
+const infoBoxData = ref("");
+const child = ref(null);
+const items = ref(DadosBrasil);
+const ref_mapa = ref(null);
+
+const props = defineProps({
+  data: {
+    type: Array,
+    required: true,
+  },
+  title: {
+    type: String,
+    required: false,
+    default: "PARTICIPANTES POR ESTADO - BRASIL",
+  },
+  subtitle: {
+    type: String,
+    required: false,
+    default: "Número de participantes do evento por estado brasileiro",
+  },
+});
+
+const mapclick = (event) => {
+  // console.log("mapa-brasil: mapclick");
+  if (event.target.tagName == "svg") {
+    resetSelectionAction();
+    infoBoxActive.value = false;
+  }
+};
+const onStateSelectedEvent = (args) => {
+  // console.log("mapa-brasil: onStateSelectedEvent: ", args);
+  selected.value = args.src.item.uf;
+  resetSelectionAction();
+  args.enable();
+};
+const onStateMouseOverEvent = (args) => {
+  // console.log("mapa-brasil: onStateMouseOverEvent: ", args);
+  setInfoBoxPosition({ x: args.event.pageX, y: args.event.pageY });
+  setInfoBoxData([
+    args.src.item.name,
+    args.src.item.regional,
+    args.src.item.altText,
+  ]);
+  infoBoxActive.value = true;
+};
+const resetSelectionAction = () => {
+  // console.log("mapa-brasil: resetSelectionAction", child.value);
+  for (let index = 0; index < child.value.length; index++) {
+    child.value[index].resetAction();
+  }
+};
+const setInfoBoxPosition = (args) => {
+  infoBoxPosX.value = args.x + 30;
+  infoBoxPosY.value = args.y;
+};
+const setInfoBoxData = (args) => {
+  infoBoxData.value = args.filter(function (a) {
+    return a !== "" ? a : null;
+  });
+};
+const hideInfoBox = (args) => {
+  if (
+    ["DIV", "SVG"].indexOf(args.target.tagName.toString().toUpperCase()) > -1
+  ) {
+    infoBoxActive.value = false;
+  }
+};
+
+onMounted(() => {
+  const dataOrganizada = [...props.data].sort(
+    (a, b) => a?.total_pessoas_por_estado - b?.total_pessoas_por_estado
+  );
+  const maiorNumero =
+    dataOrganizada[dataOrganizada.length - 1]?.total_pessoas_por_estado;
+  const faixa = maiorNumero / 5;
+  const colors = ["#f6a0a1", "#f27e7f", "#ee585a", "#EB3537", "#c31315"];
+  items.value.forEach((item) => {
+    const estado = dataOrganizada.find((e) => e.estado === item.uf);
+    if (estado) {
+      const indexFaixa = Math.ceil(estado?.total_pessoas_por_estado / faixa);
+      item.color = colors[indexFaixa - 1];
+      item.pessoas = estado.total_pessoas_por_estado;
+    } else {
+      item.color = "#494949";
+      item.pessoas = 0;
+    }
+  });
+});
+
+const dadosEstado = computed(() => {
+  const estado = items.value.find((item) => item.uf === selected.value);
+  return estado;
+});
+
+const downloadImage = () => {
+  const svgString = new XMLSerializer().serializeToString(ref_mapa.value);
+  const base64String = btoa(svgString);
+  base64ToJPEG(base64String, props.title);
+};
+</script>
+
+<style lang="scss">
+svg text {
+  fill: var(--default-stroke);
+  font-family: monospace;
+}
+
+.mapa-svg-estados {
+  fill: var(--default-fill);
+  -webkit-transition: 0.8s ease;
+  -moz-transition: 0.8s ease;
+  -ms-transition: 0.8s ease;
+  -o-transition: 0.8s ease;
+  transition: 0.8s ease;
+  stroke-dasharray: 180%;
+  stroke-dashoffset: -120%;
+  stroke-width: 1px;
+  stroke: var(--default-stroke);
+  text {
+    fill: var(--default-stroke);
+    stroke: none !important;
+  }
+}
+
+.mapa-svg-estados:hover {
+  cursor: pointer;
+  fill: #8d1012 !important;
+}
+
+.mapa-svg-estados-active {
+  cursor: pointer;
+  stroke: #ffffff;
+  fill: #ffc712 !important;
+  stroke-dashoffset: 0%;
+  transition: 0.8s ease;
+  -webkit-transition: 0.8s ease;
+  -moz-transition: 0.8s ease;
+  -ms-transition: 0.8s ease;
+  -o-transition: 0.8s ease;
+  text {
+    fill: #ffffff;
+    stroke: none !important;
+  }
+}
+</style>

+ 110 - 0
src/components/charts/maps/MapaEstado.vue

@@ -0,0 +1,110 @@
+<template>
+  <g
+    ref="chart_ref"
+    :class="[classObject]"
+    :data-regional="item.regional"
+    @click="clickAction"
+  >
+    <path :d="item.svgData" />
+    <text
+      :transform="item.textData"
+      class="item text-weight-medium"
+      style="font-size: 10px"
+      fill="white"
+    >
+      {{ item.pessoas || 0 }}
+    </text>
+    <circle
+      v-if="showCircleInfo"
+      :cy="item.circleData.cy"
+      :cx="item.circleData.cx"
+      r="10"
+      :class="[counterClassObject]"
+    />
+  </g>
+</template>
+
+<script setup>
+import { computed, onMounted, ref } from "vue";
+
+const props = defineProps({
+  item: {
+    type: Object,
+    default: () => ({
+      altText: "--",
+    }),
+  },
+  showCircleInfo: {
+    type: Boolean,
+    default: true,
+  },
+});
+
+const emit = defineEmits(["stateSelectedEvent", "stateMouseOverEvent"]);
+
+const active = ref(false);
+const counterActive = ref(false);
+const altText = ref(props.item.altText || "-");
+const chart_ref = ref(null);
+
+const classObject = computed(() => {
+  return active.value ? ["mapa-svg-estados-active"] : ["mapa-svg-estados"];
+});
+
+const counterClassObject = computed(() => {
+  return counterActive.value ? ["sphere active"] : ["sphere"];
+});
+
+// const pad = (value) => {
+//   if (!value) return "";
+//   return ("000000000" + value).slice(-2);
+// };
+
+const clickAction = () => {
+  // console.log("mapa-estado: clickAction()");
+  emit("stateSelectedEvent", {
+    src: {
+      item: props.item,
+    },
+    enable,
+  });
+};
+const resetAction = () => {
+  disable();
+};
+const disable = () => {
+  // console.log("mapa-estado: disabled ", props.item.uf);
+  active.value = false;
+  hideCounter();
+};
+
+const enable = () => {
+  // console.log("mapa-estado: enable ");
+  active.value = true;
+  showCounter();
+};
+const setAltText = (val) => {
+  altText.value = val;
+};
+const hideCounter = () => {
+  counterActive.value = false;
+};
+const showCounter = () => {
+  counterActive.value = true;
+};
+
+onMounted(() => {
+  altText.value = props.item.altText;
+});
+
+defineExpose({
+  resetAction,
+  setAltText,
+});
+</script>
+
+<style>
+.item {
+  fill: rgb(240, 221, 221);
+}
+</style>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 14 - 0
src/components/charts/maps/Paraguai/DadosParaguai.js


+ 225 - 0
src/components/charts/maps/Paraguai/MapaParaguai.vue

@@ -0,0 +1,225 @@
+<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
+<template>
+  <div>
+    <q-card style="height: 100%">
+      <q-card-section class="row justify-between">
+        <div class="column">
+          <span class="text-bold text-description q-mb-sm">
+            {{ props.title }}
+          </span>
+          <span>{{ props.subtitle }}</span>
+        </div>
+        <q-btn
+          icon="mdi-tray-arrow-down"
+          dense
+          flat
+          class="q-my-auto"
+          @click="downloadImage"
+        />
+      </q-card-section>
+
+      <q-separator inset />
+      <q-card-section>
+        <div style="display: flex; flex-direction: column">
+          <div @mouseover="hideInfoBox">
+            <svg
+              ref="ref_mapa"
+              xmlns="http://www.w3.org/2000/svg"
+              class="mapa-svg-estados"
+              viewBox="-100 -60 1250 1250"
+              @click="mapclick"
+            >
+              <g id="mapa-svg-area">
+                <MapaEstado
+                  v-for="item in items"
+                  ref="child"
+                  :key="item.id"
+                  :item="item"
+                  :show-circle-info="showCircleInfo"
+                  :class="item.classObject"
+                  :style="item.color ? `fill: ${item.color}` : ''"
+                  @state-selected-event="onStateSelectedEvent"
+                  @state-mouse-over-event="onStateMouseOverEvent"
+                ></MapaEstado>
+              </g>
+            </svg>
+          </div>
+          <div class="bg-background--2 q-pa-md" style="border-radius: 0.5rem">
+            <div
+              v-if="selected"
+              style="display: flex; flex-direction: column; gap: 0.5rem"
+            >
+              <span> <b> Estado:</b> {{ dadosEstado.name }} </span>
+              <span><b>Participantes:</b> {{ dadosEstado.pessoas || 0 }}</span>
+            </div>
+            <div
+              v-else
+              style="display: flex; flex-direction: column; gap: 0.5rem"
+            >
+              <span>
+                <b>Selecione um estado</b>
+              </span>
+            </div>
+          </div>
+        </div>
+      </q-card-section>
+    </q-card>
+  </div>
+</template>
+
+<script setup>
+import { computed, onMounted, ref } from "vue";
+import MapaEstado from "../MapaEstado.vue";
+import { DadosParaguai } from "./DadosParaguai";
+import { base64ToJPEG } from "src/helpers/convertBase64Image";
+
+const selected = ref(null);
+const showCircleInfo = ref(false);
+const infoBoxActive = ref(false);
+const infoBoxPosX = ref(0);
+const infoBoxPosY = ref(0);
+const infoBoxData = ref("");
+const child = ref(null);
+const items = ref(DadosParaguai);
+const ref_mapa = ref(null);
+
+const props = defineProps({
+  data: {
+    type: Array,
+    required: true,
+  },
+  title: {
+    type: String,
+    required: false,
+    default: "PARTICIPANTES POR DEPARTAMENTO - PARAGUAI",
+  },
+  subtitle: {
+    type: String,
+    required: false,
+    default: "Número de participantes do evento por departamento do Paraguai",
+  },
+});
+
+const mapclick = (event) => {
+  // console.log("mapa-brasil: mapclick");
+  if (event.target.tagName == "svg") {
+    resetSelectionAction();
+    infoBoxActive.value = false;
+  }
+};
+const onStateSelectedEvent = (args) => {
+  // console.log("mapa-brasil: onStateSelectedEvent: ", args);
+  selected.value = args.src.item.id;
+  resetSelectionAction();
+  args.enable();
+};
+const onStateMouseOverEvent = (args) => {
+  // console.log("mapa-brasil: onStateMouseOverEvent: ", args);
+  setInfoBoxPosition({ x: args.event.pageX, y: args.event.pageY });
+  setInfoBoxData([
+    args.src.item.name,
+    args.src.item.regional,
+    args.src.item.altText,
+  ]);
+  infoBoxActive.value = true;
+};
+const resetSelectionAction = () => {
+  // console.log("mapa-brasil: resetSelectionAction", child.value);
+  for (let index = 0; index < child.value.length; index++) {
+    child.value[index].resetAction();
+  }
+};
+const setInfoBoxPosition = (args) => {
+  infoBoxPosX.value = args.x + 30;
+  infoBoxPosY.value = args.y;
+};
+const setInfoBoxData = (args) => {
+  infoBoxData.value = args.filter(function (a) {
+    return a !== "" ? a : null;
+  });
+};
+const hideInfoBox = (args) => {
+  if (
+    ["DIV", "SVG"].indexOf(args.target.tagName.toString().toUpperCase()) > -1
+  ) {
+    infoBoxActive.value = false;
+  }
+};
+
+onMounted(() => {
+  const dataOrganizada = [...props.data].sort(
+    (a, b) => a?.total_pessoas_por_estado - b?.total_pessoas_por_estado
+  );
+  const maiorNumero =
+    dataOrganizada[dataOrganizada.length - 1]?.total_pessoas_por_estado;
+  const faixa = maiorNumero / 5;
+  const colors = ["#f6a0a1", "#f27e7f", "#ee585a", "#EB3537", "#c31315"];
+  items.value.forEach((item) => {
+    const estado = dataOrganizada.find((e) => e.estado_id === item.id);
+    if (estado) {
+      const indexFaixa = Math.ceil(estado?.total_pessoas_por_estado / faixa);
+      item.color = colors[indexFaixa - 1];
+      item.pessoas = estado.total_pessoas_por_estado;
+    } else {
+      item.color = "#494949";
+      item.pessoas = 0;
+    }
+  });
+});
+
+const dadosEstado = computed(() => {
+  const estado = items.value.find((item) => item.id === selected.value);
+  return estado;
+});
+
+const downloadImage = () => {
+  const svgString = new XMLSerializer().serializeToString(ref_mapa.value);
+  const base64String = btoa(svgString);
+  base64ToJPEG(base64String, props.title);
+};
+</script>
+
+<style lang="scss">
+svg text {
+  fill: var(--default-stroke);
+  font-family: monospace;
+}
+
+.mapa-svg-estados {
+  fill: var(--default-fill);
+  -webkit-transition: 0.8s ease;
+  -moz-transition: 0.8s ease;
+  -ms-transition: 0.8s ease;
+  -o-transition: 0.8s ease;
+  transition: 0.8s ease;
+  stroke-dasharray: 180%;
+  stroke-dashoffset: -120%;
+  stroke-width: 1px;
+  stroke: var(--default-stroke);
+  text {
+    fill: var(--default-stroke);
+    stroke: none !important;
+  }
+}
+
+.mapa-svg-estados:hover {
+  cursor: pointer;
+  fill: #8d1012 !important;
+}
+
+.mapa-svg-estados-active {
+  cursor: pointer;
+  stroke: #ffffff;
+  fill: #ffc712 !important;
+  stroke-dashoffset: 0%;
+  transition: 0.8s ease;
+  -webkit-transition: 0.8s ease;
+  -moz-transition: 0.8s ease;
+  -ms-transition: 0.8s ease;
+  -o-transition: 0.8s ease;
+  text {
+    fill: #ffffff;
+    stroke: none !important;
+  }
+}
+</style>

+ 101 - 0
src/components/charts/mini/MiniBarChart.vue

@@ -0,0 +1,101 @@
+<template>
+  <Bar
+    :id="id"
+    ref="chart_ref"
+    :options="chartOptions"
+    :data="computedChartData"
+  />
+</template>
+
+<script setup>
+import {
+  Chart as ChartJS,
+  CategoryScale,
+  LinearScale,
+  BarElement,
+  Tooltip,
+} from "chart.js";
+import { computed, useTemplateRef } from "vue";
+import { Bar } from "vue-chartjs";
+import { getCssVar } from "quasar";
+
+ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip);
+
+const chart_ref = useTemplateRef(null);
+
+const { data, barColor, horizontal, showTooltip } = defineProps({
+  data: {
+    type: Array,
+    required: true,
+  },
+  barColor: {
+    type: String,
+    default: () => getCssVar("primary"),
+  },
+  horizontal: {
+    type: Boolean,
+    default: false,
+  },
+  showTooltip: {
+    type: Boolean,
+    default: true,
+  },
+});
+
+const chartOptions = computed(() => ({
+  responsive: true,
+  maintainAspectRatio: false,
+  indexAxis: horizontal ? "y" : "x",
+
+  plugins: {
+    legend: {
+      display: false,
+    },
+    tooltip: {
+      enabled: showTooltip,
+      displayColors: false,
+      callbacks: {
+        label: (context) => `${context.raw}`,
+      },
+    },
+  },
+
+  scales: {
+    x: {
+      display: false,
+      grid: {
+        display: false,
+      },
+    },
+    y: {
+      display: false,
+      grid: {
+        display: false,
+      },
+      beginAtZero: true,
+    },
+  },
+
+  animation: {
+    duration: 750,
+    easing: "easeOutQuad",
+  },
+}));
+
+const computedChartData = computed(() => ({
+  labels: Array(data.length).fill(""),
+  datasets: [
+    {
+      data: data,
+      backgroundColor: barColor,
+      borderRadius: 2,
+      barThickness: 8,
+      maxBarThickness: 10,
+    },
+  ],
+}));
+
+defineExpose({
+  chart_ref,
+});
+</script>

+ 116 - 0
src/components/charts/mini/MiniLineChart.vue

@@ -0,0 +1,116 @@
+<template>
+  <Line ref="chart_ref" :options="chartOptions" :data="computedChartData" />
+</template>
+
+<script setup>
+import {
+  Chart as ChartJS,
+  CategoryScale,
+  LinearScale,
+  LineElement,
+  PointElement,
+  Tooltip,
+  Filler,
+} from "chart.js";
+import { computed, useTemplateRef } from "vue";
+import { Line } from "vue-chartjs";
+import { getCssVar } from "quasar";
+
+ChartJS.register(
+  CategoryScale,
+  LinearScale,
+  LineElement,
+  PointElement,
+  Tooltip,
+  Filler,
+);
+
+const chart_ref = useTemplateRef(null);
+
+const { data, lineColor, fillColor, showTooltip, showPoints } = defineProps({
+  data: {
+    type: Array,
+    required: true,
+  },
+  lineColor: {
+    type: String,
+    default: () => getCssVar("primary"),
+  },
+  fillColor: {
+    type: String,
+    default: "rgba(25, 118, 210, 0.1)",
+  },
+  showTooltip: {
+    type: Boolean,
+    default: true,
+  },
+  showPoints: {
+    type: Boolean,
+    default: false,
+  },
+});
+
+const chartOptions = computed(() => ({
+  responsive: true,
+  maintainAspectRatio: false,
+
+  plugins: {
+    legend: {
+      display: false,
+    },
+    tooltip: {
+      enabled: showTooltip,
+      displayColors: false,
+      callbacks: {
+        label: (context) => `${context.raw}`,
+      },
+    },
+  },
+
+  scales: {
+    x: {
+      display: false,
+      grid: { display: false },
+    },
+    y: {
+      display: false,
+      grid: { display: false },
+      beginAtZero: true,
+    },
+  },
+
+  elements: {
+    line: {
+      tension: 0.4,
+      borderWidth: 2,
+    },
+    point: {
+      radius: showPoints ? 3 : 0,
+      hitRadius: 5,
+      borderWidth: 0,
+      backgroundColor: showPoints ? lineColor : "rgba(0,0,0,0)",
+    },
+  },
+
+  animation: {
+    duration: 750,
+    easing: "easeOutQuad",
+  },
+}));
+
+const computedChartData = computed(() => ({
+  labels: Array(data?.length)?.fill(""),
+  datasets: [
+    {
+      data: data,
+      borderColor: lineColor,
+      backgroundColor: fillColor,
+      fill: true,
+    },
+  ],
+}));
+
+defineExpose({
+  chart_ref,
+});
+</script>

+ 267 - 0
src/components/charts/normal/BarChart.vue

@@ -0,0 +1,267 @@
+<template>
+  <div v-bind="$attrs" class="chart-wrapper full-width full-height">
+    <q-resize-observer @resize="onResize" />
+    <div v-if="hasData" class="chart-container">
+      <Bar
+        ref="chart_ref"
+        :options="chartBarOptions"
+        :data="chartBarData"
+        :plugins="[ChartDataLabels]"
+      />
+    </div>
+    <div v-else class="no-data-container">
+      <span :class="textColor">{{ $t("http.errors.no_records_found") }}</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Bar } from "vue-chartjs";
+import {
+  Chart as ChartJS,
+  Title,
+  Tooltip,
+  Legend,
+  BarElement,
+  CategoryScale,
+  LinearScale,
+} from "chart.js";
+import ChartDataLabels from "chartjs-plugin-datalabels";
+import { base64ToJPEG } from "src/helpers/convertBase64Image";
+import { useQuasar, colors, getCssVar } from "quasar";
+
+ChartJS.register(
+  Title,
+  Tooltip,
+  Legend,
+  BarElement,
+  CategoryScale,
+  LinearScale,
+);
+
+const $q = useQuasar();
+const chart_ref = ref(null);
+const { lighten } = colors;
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({
+      chart_data: [],
+      current_total: 0,
+    }),
+  },
+  dataSetLabel: {
+    type: String,
+    default: "Quantidade",
+  },
+  labelX: {
+    type: String,
+    default: "Categorias",
+  },
+  labelY: {
+    type: String,
+    default: "Valores",
+  },
+  showLegend: {
+    type: Boolean,
+    default: false,
+  },
+  title: {
+    type: String,
+    default: "Título",
+  },
+  backgroundColors: {
+    type: Array,
+    default: null,
+  },
+});
+
+const onResize = () => {
+  if (chart_ref.value?.chart) {
+    setTimeout(() => {
+      chart_ref.value.chart.resize();
+    }, 50);
+  }
+};
+
+const textColor = computed(() => {
+  return $q.dark.isActive ? "text-white" : "text-black";
+});
+
+const labelColor = computed(() => {
+  return $q.dark.isActive ? "#ffffff" : "#000000";
+});
+
+const gridColor = computed(() => {
+  return $q.dark.isActive ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)";
+});
+
+const dataLabelColor = computed(() => {
+  return $q.dark.isActive ? "#ffffff" : "#000000";
+});
+
+const hasData = computed(() => {
+  return props.data?.chart_data && props.data.chart_data.length > 0;
+});
+
+const chartLabels = computed(() => {
+  return props.data?.chart_data?.map((item) => item.label) || [];
+});
+
+const chartValues = computed(() => {
+  return props.data?.chart_data?.map((item) => item.value) || [];
+});
+
+const chartThemeColors = computed(() => {
+  if (props.backgroundColors) {
+    return props.backgroundColors;
+  }
+
+  const primaryColor = getCssVar("primary");
+  if (!primaryColor) return [];
+
+  const numColors = chartValues.value.length;
+  const step = numColors > 0 ? 50 / numColors : 0;
+  return Array.from({ length: numColors }, (_, i) =>
+    lighten(primaryColor, i * step),
+  );
+});
+
+const chartBarData = computed(() => ({
+  labels: chartLabels.value,
+  datasets: [
+    {
+      label: props.dataSetLabel,
+      data: chartValues.value,
+      backgroundColor: chartThemeColors.value,
+      borderColor: chartThemeColors.value,
+      borderWidth: 1,
+    },
+  ],
+}));
+
+const chartBarOptions = computed(() => ({
+  responsive: true,
+  maintainAspectRatio: false,
+  plugins: {
+    legend: {
+      display: props.showLegend,
+      position: "top",
+      labels: {
+        color: labelColor.value,
+        font: {
+          size: 14,
+        },
+      },
+    },
+    datalabels: {
+      color: dataLabelColor.value,
+      anchor: "end",
+      align: "top",
+      offset: 4,
+      font: {
+        size: 12,
+        weight: "bold",
+      },
+      formatter: (value) => {
+        return value > 0 ? value : "";
+      },
+    },
+    tooltip: {
+      backgroundColor: $q.dark.isActive
+        ? "rgba(0, 0, 0, 0.8)"
+        : "rgba(255, 255, 255, 0.9)",
+      titleColor: labelColor.value,
+      bodyColor: labelColor.value,
+      borderColor: labelColor.value,
+      borderWidth: 1,
+    },
+  },
+  scales: {
+    x: {
+      display: true,
+      title: {
+        display: !!props.labelX,
+        text: props.labelX,
+        color: labelColor.value,
+        font: {
+          size: 14,
+        },
+      },
+      grid: {
+        color: gridColor.value,
+        tickColor: gridColor.value,
+      },
+      ticks: {
+        color: labelColor.value,
+        font: {
+          size: 12,
+        },
+      },
+    },
+    y: {
+      display: true,
+      title: {
+        display: !!props.labelY,
+        text: props.labelY,
+        color: labelColor.value,
+        font: {
+          size: 14,
+        },
+      },
+      suggestedMin: 0,
+      grid: {
+        color: gridColor.value,
+        tickColor: gridColor.value,
+      },
+      ticks: {
+        color: labelColor.value,
+        font: {
+          size: 12,
+        },
+        stepSize: 1,
+      },
+    },
+  },
+}));
+
+const downloadImage = () => {
+  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
+  base64ToJPEG(image, props.title || "bar-chart");
+};
+
+defineExpose({
+  downloadImage,
+  chart_ref,
+});
+</script>
+
+<style scoped>
+.chart-wrapper {
+  position: relative;
+}
+
+.chart-container {
+  position: absolute;
+  top: 10px;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.no-data-container {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 1.5rem;
+}
+</style>

+ 204 - 0
src/components/charts/normal/DoughnutChart.vue

@@ -0,0 +1,204 @@
+<template>
+  <div v-bind="$attrs" class="chart-wrapper full-width full-height">
+    <q-resize-observer @resize="onResize" />
+    <div v-if="hasData" class="chart-container">
+      <Doughnut
+        ref="chart_ref"
+        :options="chartPieOptions"
+        :data="chartPieData"
+        :plugins="[ChartDataLabels]"
+      />
+    </div>
+    <div v-else class="no-data-container">
+      <span :class="textColor">{{ $t("http.errors.no_records_found") }}</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
+import { Doughnut } from "vue-chartjs";
+import ChartDataLabels from "chartjs-plugin-datalabels";
+import { base64ToJPEG } from "src/helpers/convertBase64Image";
+import { useQuasar, colors, getCssVar } from "quasar";
+
+ChartJS.register(ArcElement, Tooltip, Legend);
+
+const $q = useQuasar();
+const { lighten } = colors;
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({
+      chart_data: [],
+    }),
+  },
+  backgroundColors: {
+    type: Array,
+    default: null,
+  },
+  title: {
+    type: String,
+    default: "doughnut-chart",
+  },
+  dataSetLabel: {
+    type: String,
+    default: "Dados",
+  },
+});
+
+const chart_ref = ref(null);
+
+const onResize = () => {
+  if (chart_ref.value?.chart) {
+    setTimeout(() => {
+      chart_ref.value.chart.resize();
+    }, 50);
+  }
+};
+
+const textColor = computed(() => {
+  return $q.dark.isActive ? "text-white" : "text-black";
+});
+
+const labelColor = computed(() => {
+  return $q.dark.isActive ? "#ffffff" : "#000000";
+});
+
+const dataLabelColor = computed(() => {
+  return $q.dark.isActive ? "#ffffff" : "#000000";
+});
+
+const hasData = computed(() => {
+  return props.data?.chart_data && props.data.chart_data.length > 0;
+});
+
+const chartLabels = computed(() => {
+  return props.data?.chart_data?.map((item) => item.label) || [];
+});
+
+const chartValues = computed(() => {
+  return props.data?.chart_data?.map((item) => item.value) || [];
+});
+
+const chartPercentages = computed(() => {
+  const total = props.data?.current_total || 0;
+  if (total === 0) return [];
+
+  return (
+    props.data?.chart_data?.map((item) =>
+      Math.round((item.value / total) * 100),
+    ) || []
+  );
+});
+
+const chartThemeColors = computed(() => {
+  if (props.backgroundColors) {
+    return props.backgroundColors;
+  }
+
+  const primaryColor = getCssVar("primary");
+  if (!primaryColor) return [];
+
+  const numColors = chartValues.value.length;
+  const step = numColors > 0 ? 50 / numColors : 0;
+  return Array.from({ length: numColors }, (_, i) =>
+    lighten(primaryColor, i * step),
+  );
+});
+
+const chartPieData = computed(() => ({
+  labels: chartLabels.value,
+  datasets: [
+    {
+      label: props.dataSetLabel,
+      data: chartPercentages.value,
+      backgroundColor: chartThemeColors.value,
+    },
+  ],
+}));
+
+const chartPieOptions = computed(() => ({
+  responsive: true,
+  maintainAspectRatio: false,
+  plugins: {
+    legend: {
+      position: "bottom",
+      labels: {
+        color: labelColor.value,
+        font: {
+          size: 12,
+        },
+        padding: 20,
+      },
+    },
+    datalabels: {
+      color: dataLabelColor.value,
+      font: {
+        size: 12,
+        weight: "bold",
+      },
+      formatter: (value) => {
+        return value > 0 ? value + "%" : "";
+      },
+    },
+    tooltip: {
+      backgroundColor: $q.dark.isActive
+        ? "rgba(0, 0, 0, 0.8)"
+        : "rgba(255, 255, 255, 0.9)",
+      titleColor: labelColor.value,
+      bodyColor: labelColor.value,
+      borderColor: labelColor.value,
+      borderWidth: 1,
+      callbacks: {
+        label: (context) => {
+          const label = context.label || "";
+          const percentage = context.parsed;
+          const actualValue = chartValues.value[context.dataIndex];
+          return `${label}: ${actualValue} (${percentage}%)`;
+        },
+      },
+    },
+  },
+}));
+
+const downloadImage = () => {
+  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
+  base64ToJPEG(image, props.title || "pie-chart");
+};
+
+defineExpose({
+  downloadImage,
+  chart_ref,
+});
+</script>
+
+<style scoped>
+.chart-wrapper {
+  position: relative;
+}
+
+.chart-container {
+  position: absolute;
+  top: 10px;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.no-data-container {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 1.5rem;
+}
+</style>

+ 265 - 0
src/components/charts/normal/LineChart.vue

@@ -0,0 +1,265 @@
+<template>
+  <div v-bind="$attrs" class="chart-wrapper full-width full-height">
+    <q-resize-observer @resize="onResize" />
+    <div v-if="hasData" class="chart-container">
+      <Line
+        ref="chart_ref"
+        :options="chartLineOptions"
+        :data="chartLineData"
+        :plugins="[ChartDataLabels]"
+      />
+    </div>
+    <div v-else class="no-data-container">
+      <span :class="textColor">{{ $t("http.errors.no_records_found") }}</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Line } from "vue-chartjs";
+import {
+  Chart as ChartJS,
+  Title,
+  Tooltip,
+  Legend,
+  LineElement,
+  PointElement,
+  CategoryScale,
+  LinearScale,
+} from "chart.js";
+import ChartDataLabels from "chartjs-plugin-datalabels";
+import { base64ToJPEG } from "src/helpers/convertBase64Image";
+import { useQuasar, getCssVar, colors } from "quasar";
+
+ChartJS.register(
+  Title,
+  Tooltip,
+  Legend,
+  LineElement,
+  PointElement,
+  CategoryScale,
+  LinearScale,
+);
+
+const $q = useQuasar();
+const { lighten } = colors;
+const chart_ref = ref(null);
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({
+      chart_data: [],
+    }),
+  },
+  title: {
+    type: String,
+    default: "",
+  },
+  dataSetLabel: {
+    type: String,
+    default: "Valores",
+  },
+  labelX: {
+    type: String,
+    default: "Categorias",
+  },
+  labelY: {
+    type: String,
+    default: "Valores",
+  },
+  backgroundColors: {
+    type: Array,
+    default: null,
+  },
+});
+
+const onResize = () => {
+  if (chart_ref.value?.chart) {
+    setTimeout(() => {
+      chart_ref.value.chart.resize();
+    }, 50);
+  }
+};
+
+const textColor = computed(() => {
+  return $q.dark.isActive ? "text-white" : "text-black";
+});
+
+const labelColor = computed(() => {
+  return $q.dark.isActive ? "#ffffff" : "#000000";
+});
+
+const gridColor = computed(() => {
+  return $q.dark.isActive ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)";
+});
+
+const dataLabelColor = computed(() => {
+  return $q.dark.isActive ? "#ffffff" : "#000000";
+});
+
+const hasData = computed(() => {
+  return props.data?.chart_data && props.data.chart_data.length > 0;
+});
+
+const chartLabels = computed(() => {
+  return props.data?.chart_data?.map((item) => item.label) || [];
+});
+
+const chartValues = computed(() => {
+  return props.data?.chart_data?.map((item) => item.value) || [];
+});
+
+const chartThemeColors = computed(() => {
+  if (props.backgroundColors) {
+    return props.backgroundColors;
+  }
+
+  const primaryColor = getCssVar("primary");
+  if (!primaryColor) return [];
+
+  const numColors = chartValues.value.length;
+  const step = numColors > 0 ? 50 / numColors : 0;
+  return Array.from({ length: numColors }, (_, i) =>
+    lighten(primaryColor, i * step),
+  );
+});
+
+const chartLineData = computed(() => ({
+  labels: chartLabels.value,
+  datasets: [
+    {
+      label: props.dataSetLabel,
+      data: chartValues.value,
+      borderColor: chartThemeColors.value[0],
+      backgroundColor: chartThemeColors.value,
+      fill: false,
+      cubicInterpolationMode: "monotone",
+      tension: 0.4,
+    },
+  ],
+}));
+
+const chartLineOptions = computed(() => ({
+  responsive: true,
+  maintainAspectRatio: false,
+  plugins: {
+    legend: {
+      display: false,
+    },
+    title: {
+      display: !!props.title,
+      text: props.title,
+      color: labelColor.value,
+      font: {
+        size: 16,
+      },
+    },
+    datalabels: {
+      color: dataLabelColor.value,
+      anchor: "end",
+      align: "top",
+      offset: 4,
+      font: {
+        size: 12,
+        weight: "bold",
+      },
+      formatter: (value) => {
+        return value > 0 ? value : "";
+      },
+    },
+    tooltip: {
+      backgroundColor: $q.dark.isActive
+        ? "rgba(0, 0, 0, 0.8)"
+        : "rgba(255, 255, 255, 0.9)",
+      titleColor: labelColor.value,
+      bodyColor: labelColor.value,
+      borderColor: labelColor.value,
+      borderWidth: 1,
+    },
+  },
+  interaction: {
+    intersect: false,
+  },
+  scales: {
+    x: {
+      display: true,
+      title: {
+        display: !!props.labelX,
+        text: props.labelX,
+        color: labelColor.value,
+        font: {
+          size: 14,
+        },
+      },
+      grid: {
+        color: gridColor.value,
+        tickColor: gridColor.value,
+      },
+      ticks: {
+        color: labelColor.value,
+      },
+    },
+    y: {
+      display: true,
+      title: {
+        display: !!props.labelY,
+        text: props.labelY,
+        color: labelColor.value,
+        font: {
+          size: 14,
+        },
+      },
+      suggestedMin: 0,
+      grid: {
+        color: gridColor.value,
+        tickColor: gridColor.value,
+      },
+      ticks: {
+        color: labelColor.value,
+        stepSize: 1,
+      },
+    },
+  },
+}));
+
+const downloadImage = () => {
+  if (!chart_ref.value?.chart) return;
+  const image = chart_ref.value.chart.toBase64Image("image/jpeg", 1);
+  base64ToJPEG(image, props.title || "line-chart");
+};
+
+defineExpose({
+  downloadImage,
+  chart_ref,
+});
+</script>
+
+<style scoped>
+.chart-wrapper {
+  position: relative;
+}
+
+.chart-container {
+  position: absolute;
+  top: 10px;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.no-data-container {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 1.5rem;
+}
+</style>

+ 205 - 0
src/components/charts/normal/PieChart.vue

@@ -0,0 +1,205 @@
+<template>
+  <div v-bind="$attrs" class="chart-wrapper full-width full-height">
+    <q-resize-observer @resize="onResize" />
+    <div v-if="hasData" class="chart-container">
+      <Pie
+        ref="chart_ref"
+        :options="chartPieOptions"
+        :data="chartPieData"
+        :plugins="[ChartDataLabels]"
+      />
+    </div>
+    <div v-else class="no-data-container">
+      <span :class="textColor">{{ $t("http.errors.no_records_found") }}</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
+import { Pie } from "vue-chartjs";
+import ChartDataLabels from "chartjs-plugin-datalabels";
+import { base64ToJPEG } from "src/helpers/convertBase64Image";
+import { useQuasar, getCssVar, colors } from "quasar";
+
+ChartJS.register(ArcElement, Tooltip, Legend);
+
+const $q = useQuasar();
+const { lighten } = colors;
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({
+      chart_data: [],
+      current_total: 0,
+    }),
+  },
+  dataSetLabel: {
+    type: String,
+    default: "Dados",
+  },
+  title: {
+    type: String,
+    default: "Título",
+  },
+  backgroundColors: {
+    type: Array,
+    default: null,
+  },
+});
+
+const chart_ref = ref(null);
+
+const onResize = () => {
+  if (chart_ref.value?.chart) {
+    setTimeout(() => {
+      chart_ref.value.chart.resize();
+    }, 50);
+  }
+};
+
+const textColor = computed(() => {
+  return $q.dark.isActive ? "text-white" : "text-black";
+});
+
+const labelColor = computed(() => {
+  return $q.dark.isActive ? "#ffffff" : "#000000";
+});
+
+const dataLabelColor = computed(() => {
+  return $q.dark.isActive ? "#ffffff" : "#000000";
+});
+
+const hasData = computed(() => {
+  return props.data?.chart_data && props.data.chart_data.length > 0;
+});
+
+const chartLabels = computed(() => {
+  return props.data?.chart_data?.map((item) => item.label) || [];
+});
+
+const chartValues = computed(() => {
+  return props.data?.chart_data?.map((item) => item.value) || [];
+});
+
+const chartPercentages = computed(() => {
+  const total = props.data?.current_total || 0;
+  if (total === 0) return [];
+
+  return (
+    props.data?.chart_data?.map((item) =>
+      Math.round((item.value / total) * 100),
+    ) || []
+  );
+});
+
+const chartThemeColors = computed(() => {
+  if (props.backgroundColors) {
+    return props.backgroundColors;
+  }
+
+  const primaryColor = getCssVar("primary");
+  if (!primaryColor) return [];
+
+  const numColors = chartValues.value.length;
+  const step = numColors > 0 ? 50 / numColors : 0;
+  return Array.from({ length: numColors }, (_, i) =>
+    lighten(primaryColor, i * step),
+  );
+});
+
+const chartPieData = computed(() => ({
+  labels: chartLabels.value,
+  datasets: [
+    {
+      label: props.dataSetLabel,
+      data: chartPercentages.value,
+      backgroundColor: chartThemeColors.value,
+    },
+  ],
+}));
+
+const chartPieOptions = computed(() => ({
+  responsive: true,
+  maintainAspectRatio: false,
+  plugins: {
+    legend: {
+      position: "bottom",
+      labels: {
+        color: labelColor.value,
+        font: {
+          size: 12,
+        },
+        padding: 20,
+      },
+    },
+    datalabels: {
+      color: dataLabelColor.value,
+      font: {
+        size: 12,
+        weight: "bold",
+      },
+      formatter: (value) => {
+        return value > 0 ? value + "%" : "";
+      },
+    },
+    tooltip: {
+      backgroundColor: $q.dark.isActive
+        ? "rgba(0, 0, 0, 0.8)"
+        : "rgba(255, 255, 255, 0.9)",
+      titleColor: labelColor.value,
+      bodyColor: labelColor.value,
+      borderColor: labelColor.value,
+      borderWidth: 1,
+      callbacks: {
+        label: (context) => {
+          const label = context.label || "";
+          const percentage = context.parsed;
+          const actualValue = chartValues.value[context.dataIndex];
+          return `${label}: ${actualValue} (${percentage}%)`;
+        },
+      },
+    },
+  },
+}));
+
+const downloadImage = () => {
+  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
+  base64ToJPEG(image, props.title || "pie-chart");
+};
+
+defineExpose({
+  downloadImage,
+  chart_ref,
+});
+</script>
+
+<style scoped>
+.chart-wrapper {
+  position: relative;
+}
+
+.chart-container {
+  position: absolute;
+  top: 10px;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.no-data-container {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 1.5rem;
+}
+</style>

+ 103 - 0
src/components/defaults/DefaultCepInput.vue

@@ -0,0 +1,103 @@
+<template>
+  <div v-bind="$attrs">
+    <q-input
+      v-model="model"
+      outlined
+      debounce="500"
+      :disable="disable"
+      :class="disable ?? 'no-pointer-events'"
+      :readonly="readonly"
+      :label="newLabel"
+      :mask="masks.Brasil.cep"
+      :rules="rules"
+      :loading="loading"
+      :error
+      :error-message
+    />
+  </div>
+</template>
+<script setup>
+import { watch, computed, ref, nextTick } from "vue";
+import { useQuasar } from "quasar";
+import masks from "src/helpers/masks.js";
+
+const $q = useQuasar();
+
+const model = defineModel({
+  type: Number,
+});
+
+const { disable, readonly, label, rules } = defineProps({
+  disable: {
+    type: Boolean,
+    default: false,
+  },
+  readonly: {
+    type: Boolean,
+    default: false,
+  },
+  label: {
+    type: String,
+    default: "CEP",
+  },
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const emit = defineEmits([
+  "cidade",
+  "estado",
+  "rua",
+  "bairro",
+  "numero",
+  "complemento",
+  "uf",
+]);
+
+const loading = ref(false);
+
+const newLabel = computed(() => label ?? void 0);
+
+watch(
+  () => model.value,
+  async (value) => {
+    if (value < 0) {
+      model.value = 0;
+    }
+    if (value && value.length > 8) {
+      try {
+        loading.value = true;
+        const response = await fetch(`https://viacep.com.br/ws/${value}/json`);
+        const data = await response.json();
+        emit("estado", data.estado);
+        emit("uf", data.uf);
+        // this is a hack to work well with the city and state select
+        await nextTick();
+        emit("cidade", data.localidade);
+        emit("rua", data.logradouro);
+        emit("bairro", data.bairro);
+        emit("numero", null);
+        emit("complemento", null);
+      } catch (error) {
+        console.error(error);
+        $q.notify({
+          message: "CEP inválido",
+          color: "negative",
+        });
+      } finally {
+        loading.value = false;
+      }
+    }
+  },
+);
+</script>

+ 80 - 0
src/components/defaults/DefaultCurrencyInput.vue

@@ -0,0 +1,80 @@
+<template>
+  <q-input
+    ref="inputRef"
+    v-model="formattedValue"
+    outlined
+    :error-message
+    :error="!!errorMessageComp"
+    :class="disable ? 'no-pointer-events' : ''"
+    :label="newLabel"
+    :disable
+    :readonly
+  >
+  </q-input>
+</template>
+<script setup>
+import { useCurrencyInput } from "vue-currency-input";
+import { computed, watch } from "vue";
+import { useI18n } from "vue-i18n";
+
+const model = defineModel({
+  type: Number,
+});
+
+const { options, disable, readonly, label, error, errorMessage } = defineProps({
+  options: {
+    type: Object,
+    default: () => ({
+      locale: "pt-BR",
+      currency: "BRL",
+      currencyDisplay: "symbol",
+      hideCurrencySymbolOnFocus: false,
+      hideGroupingSeparatorOnFocus: false,
+      hideNegligibleDecimalDigitsOnFocus: false,
+      autoDecimalDigits: true,
+      useGrouping: true,
+      accountingSign: false,
+    }),
+  },
+  disable: {
+    type: Boolean,
+    default: false,
+  },
+  readonly: {
+    type: Boolean,
+    default: false,
+  },
+  label: {
+    type: String,
+    default: () => useI18n().t("common.terms.currency"),
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const { inputRef, formattedValue, numberValue, setValue } =
+  useCurrencyInput(options);
+
+const errorMessageComp = computed(() => {
+  numberValue.value < 0
+    ? useI18n().t("validation.rules.value_smaller_than_zero")
+    : undefined;
+
+  return errorMessage ? errorMessage : (error ?? void 0);
+});
+
+const newLabel = computed(() => (label ? label : void 0));
+
+watch(
+  () => model.value,
+  (value) => {
+    setValue(value);
+  },
+);
+</script>

+ 58 - 0
src/components/defaults/DefaultDialogHeader.vue

@@ -0,0 +1,58 @@
+<template>
+  <q-bar class="q-py-md" v-bind="$attrs" style="height: 50px">
+    <q-icon v-if="icon" :name="icon" />
+    <div>{{ title() }}</div>
+
+    <q-space />
+
+    <q-btn
+      v-if="maximizable"
+      dense
+      flat
+      :icon="!maximizedToggle ? 'mdi-arrow-expand' : 'mdi-arrow-collapse'"
+      @click="onMaximazedClick"
+    />
+
+    <q-btn dense flat icon="mdi-close" @click="emit('close')" />
+  </q-bar>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import { useI18n } from "vue-i18n";
+
+const emit = defineEmits(["maximized", "close"]);
+
+const { title, fullscreen, maximizable, icon } = defineProps({
+  title: {
+    type: Function,
+    default: () => useI18n().t("common.terms.title"),
+  },
+  fullscreen: {
+    type: Boolean,
+    default: false,
+  },
+  maximizable: {
+    type: Boolean,
+    default: false,
+  },
+  icon: {
+    type: String,
+    default: "",
+  },
+});
+
+const maximizedToggle = ref(false);
+
+const onMaximazedClick = () => {
+  maximizedToggle.value = !maximizedToggle.value;
+
+  emit("maximized", maximizedToggle.value);
+};
+
+onMounted(() => {
+  if (fullscreen) {
+    maximizedToggle.value = true;
+  }
+});
+</script>

+ 257 - 0
src/components/defaults/DefaultFilePicker.vue

@@ -0,0 +1,257 @@
+<template>
+  <q-field
+    v-model="model"
+    v-bind="$attrs"
+    borderless
+    :rules="rules"
+    :error="error"
+    :error-message="errorMessage"
+    class="custom-file-input"
+  >
+    <div class="column flex-center q-mb-sm full-width">
+      <span v-if="label" class="text-grey-6 q-mb-xs">{{ label }}</span>
+      <div
+        class="image-preview-container"
+        :class="{
+          'has-image': preview,
+          'is-dragging': isDragging,
+        }"
+        @click="pickFile"
+        @dragover="handleDragOver"
+        @dragleave="handleDragLeave"
+        @drop="handleDrop"
+      >
+        <template v-if="!preview">
+          <q-icon
+            :name="
+              isDragging
+                ? 'mdi-upload'
+                : type === 'image'
+                  ? 'mdi-image-plus'
+                  : 'mdi-file-plus'
+            "
+            size="48px"
+            color="grey-6"
+            class="absolute-center"
+          />
+          <div
+            class="text-caption text-grey-6 text-center absolute-bottom q-pb-sm q-px-md"
+          >
+            {{
+              isDragging
+                ? $t("common.ui.file.drag_and_drop")
+                : type == "image"
+                  ? $t("common.ui.file.click_select_image")
+                  : $t("common.ui.file.click_select")
+            }}
+          </div>
+        </template>
+
+        <q-img
+          v-else-if="type === 'image'"
+          :src="preview"
+          fit="cover"
+          class="full-height"
+        />
+
+        <div v-else class="position-relative column full-height flex-center">
+          <q-icon name="mdi-file-check" size="48px" color="grey-6" />
+          <div
+            class="absolute-bottom text-caption text-grey-6 text-center q-mb-sm q-px-md"
+          >
+            {{ model.name }}
+          </div>
+        </div>
+
+        <div v-if="preview" class="absolute-top-right q-ma-xs">
+          <q-btn
+            flat
+            dense
+            round
+            color="negative"
+            icon="mdi-close"
+            @click.stop="clearFile"
+          />
+        </div>
+      </div>
+
+      <q-file
+        v-show="false"
+        ref="fileInputRef"
+        v-model="model"
+        :accept="accept"
+      />
+    </div>
+  </q-field>
+</template>
+
+<script setup>
+import { ref, watch, onUnmounted, useTemplateRef } from "vue";
+
+const { label, rules, accept, type, initialImage } = defineProps({
+  label: {
+    type: String,
+    default: "Select Image",
+  },
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  accept: {
+    type: String,
+    default: "image/*",
+  },
+  type: {
+    type: String,
+    default: "image",
+  },
+  initialImage: {
+    type: String,
+    default: null,
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const model = defineModel({ type: [File, String, null], default: null });
+const base64File = defineModel("base64File", { type: String, default: null });
+
+const isDragging = ref(false);
+const fileInputRef = useTemplateRef("fileInputRef");
+const preview = ref(initialImage || null);
+let objectUrl = null;
+
+const cleanupObjectURL = () => {
+  if (objectUrl) {
+    URL.revokeObjectURL(objectUrl);
+    objectUrl = null;
+  }
+};
+onUnmounted(cleanupObjectURL);
+
+const generateBase64 = (file) => {
+  if (!file) {
+    base64File.value = null;
+    return;
+  }
+  const reader = new FileReader();
+  reader.onload = (e) => {
+    base64File.value = e.target.result;
+  };
+  reader.onerror = () => {
+    console.error("FileReader failed to read file.");
+    base64File.value = null;
+  };
+  reader.readAsDataURL(file);
+};
+
+watch(model, (newFile) => {
+  cleanupObjectURL();
+
+  if (newFile && newFile instanceof File) {
+    if (type === "image") {
+      objectUrl = URL.createObjectURL(newFile);
+      preview.value = objectUrl;
+    } else {
+      preview.value = "file_selected";
+    }
+    generateBase64(newFile);
+  } else {
+    preview.value = initialImage || null;
+    base64File.value = null;
+  }
+});
+
+const pickFile = () => {
+  fileInputRef.value?.pickFiles();
+};
+
+const clearFile = () => {
+  model.value = null;
+};
+
+const handleDragOver = (event) => {
+  event.preventDefault();
+  event.dataTransfer.dropEffect = "copy";
+  isDragging.value = true;
+};
+
+const handleDragLeave = () => {
+  isDragging.value = false;
+};
+
+const handleDrop = (event) => {
+  event.preventDefault();
+  isDragging.value = false;
+
+  const file = event.dataTransfer?.files?.[0];
+  if (!file) return;
+
+  const acceptedMime = accept;
+
+  if (acceptedMime.endsWith("/*")) {
+    const baseMime = acceptedMime.replace("/*", "");
+    if (file.type.startsWith(baseMime + "/")) {
+      model.value = file;
+    }
+  } else {
+    if (
+      acceptedMime
+        .split(",")
+        .map((m) => m.trim())
+        .includes(file.type)
+    ) {
+      model.value = file;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+@use "sass:map";
+@use "src/css/quasar.variables.scss";
+
+.image-preview-container {
+  .body--dark & {
+    --image-bg-color: #{map.get($colors-dark, "surface")};
+    --image-border-color: #{map.get($colors-dark, "primary")};
+    --image-border-hover-color: #{map.get($colors-dark, "primary-dark")};
+  }
+
+  .body--light & {
+    --image-bg-color: #{map.get($colors, "surface")};
+    --image-border-color: #{map.get($colors, "primary")};
+    --image-border-hover-color: #{map.get($colors, "primary-dark")};
+  }
+
+  width: 200px;
+  height: 200px;
+  border: 2px dashed var(--image-border-color);
+  border-radius: 4px;
+  position: relative;
+  overflow: hidden;
+  transition: all 0.3s;
+  cursor: pointer;
+
+  &.is-dragging {
+    border-color: var(--image-border-hover-color);
+    background-color: var(--image-bg-color);
+    opacity: 0.8;
+  }
+
+  &:hover {
+    border-color: var(--image-border-hover-color);
+    background-color: var(--image-bg-color);
+  }
+
+  &.has-image {
+    border-style: solid;
+  }
+}
+</style>

+ 162 - 0
src/components/defaults/DefaultInputDatePicker.vue

@@ -0,0 +1,162 @@
+<template>
+  <div v-bind="$attrs">
+    <q-input
+      ref="inputRef"
+      v-model="treatedDate"
+      :mask="inputMask"
+      :label
+      :rules
+      :dense
+      :error
+      :error-message
+      clearable
+    >
+      <template #append>
+        <q-icon
+          :name="time ? 'mdi-calendar-clock' : 'mdi-calendar'"
+          class="cursor-pointer"
+        >
+          <q-popup-proxy cover transition-show="scale" transition-hide="scale">
+            <template v-if="!time">
+              <q-date v-model="date" mask="YYYY-MM-DD">
+                <div class="row items-center justify-end">
+                  <q-btn v-close-popup label="Close" color="primary" flat />
+                </div>
+              </q-date>
+            </template>
+
+            <template v-else>
+              <q-tab-panels
+                v-model="activePanel"
+                animated
+                transition-prev="slide-right"
+                transition-next="slide-left"
+                class="bg-white"
+              >
+                <q-tab-panel name="date" class="q-pa-none">
+                  <q-date
+                    v-model="date"
+                    mask="YYYY-MM-DD HH:mm"
+                    @update:model-value="handleDateSelection"
+                  />
+                </q-tab-panel>
+
+                <q-tab-panel name="time" class="q-pa-none">
+                  <q-time v-model="date" mask="YYYY-MM-DD HH:mm" format24h />
+                </q-tab-panel>
+              </q-tab-panels>
+            </template>
+          </q-popup-proxy>
+        </q-icon>
+      </template>
+    </q-input>
+  </div>
+</template>
+
+<script setup>
+import { watch, ref, computed, useTemplateRef } from "vue";
+import { useI18n } from "vue-i18n";
+import masks from "src/helpers/masks";
+
+const { label, rules, time, dense } = defineProps({
+  label: {
+    type: String,
+    default: () => useI18n().t("common.terms.date"),
+  },
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  dense: {
+    type: Boolean,
+    default: false,
+  },
+  time: {
+    type: Boolean,
+    default: false,
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const qInputRef = useTemplateRef("inputRef", { type: String });
+
+const treatedDate = defineModel({ type: String });
+const untreatedDate = defineModel("untreatedDate", { type: String });
+
+const activePanel = ref("date");
+const date = ref();
+
+const handleDateSelection = () => {
+  if (time) {
+    activePanel.value = "time";
+  }
+};
+
+const formatDate = (value) => {
+  if (!value) return null;
+
+  const [datePart, timePart] = value.split(" ");
+  const formattedDate = datePart.split("-").reverse().join("/");
+
+  return time && timePart ? `${formattedDate} ${timePart}` : formattedDate;
+};
+
+const unformatDate = (value) => {
+  if (!value) return null;
+
+  const [datePart, timePart] = value.split(" ");
+  const formattedDate = datePart.split("/").reverse().join("-");
+
+  return time && timePart ? `${formattedDate} ${timePart}` : formattedDate;
+};
+
+const inputMask = computed(() => {
+  if (!qInputRef.value) return "";
+
+  if (time) {
+    return masks.Brasil.datetime;
+  }
+  return masks.Brasil.date;
+});
+
+watch(date, (value) => {
+  if (!value) return;
+
+  untreatedDate.value = value;
+  treatedDate.value = formatDate(value);
+});
+
+watch(treatedDate, (value) => {
+  if (!value) {
+    date.value = null;
+    untreatedDate.value = null;
+    treatedDate.value = null;
+    activePanel.value = "date";
+    return;
+  }
+  date.value = unformatDate(value);
+});
+
+watch(
+  untreatedDate,
+  (value) => {
+    if (!value) {
+      date.value = null;
+      untreatedDate.value = null;
+      treatedDate.value = null;
+      activePanel.value = "date";
+      return;
+    }
+    date.value = value;
+    treatedDate.value = formatDate(value);
+  },
+  { immediate: true },
+);
+</script>

+ 42 - 0
src/components/defaults/DefaultPasswordInput.vue

@@ -0,0 +1,42 @@
+<template>
+  <q-input
+    v-model="password"
+    v-bind="$attrs"
+    :label="$t('common.terms.password')"
+    :type="!seePassword ? 'password' : 'text'"
+    :rules
+    :error
+    :error-message
+  >
+    <template #append>
+      <q-icon
+        :name="seePassword ? 'mdi-eye-off' : 'mdi-eye'"
+        class="cursor-pointer q-ml-md"
+        @click="seePassword = !seePassword"
+      />
+    </template>
+  </q-input>
+</template>
+<script setup>
+const { rules } = defineProps({
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const password = defineModel({ type: String });
+const seePassword = defineModel("seePassword", {
+  default: false,
+  type: Boolean,
+});
+
+</script>

+ 326 - 0
src/components/defaults/DefaultTable.vue

@@ -0,0 +1,326 @@
+<template>
+  <q-table
+    v-model:fullscreen="fullscreen"
+    flat
+    row-key="id"
+    :pagination="{ rowsPerPage }"
+    :pagination-label="getPaginationLabel"
+    :rows-per-page-label="$t('common.ui.table.rows_per_page')"
+    :grid="$q.screen.lt.sm"
+    :visible-columns
+    :filter
+    :columns
+    :rows
+    :loading
+    class="softpar-table q-pa-sm"
+    @row-click="onRowClick"
+  >
+    <template #top>
+      <div
+        class="flex full-width justify-between align-center q-mb-md q-pl-sm"
+        style="gap: 1rem"
+      >
+        <q-input
+          v-if="showSearchField"
+          v-model="filter"
+          debounce="250"
+          :placeholder="$t('common.actions.search')"
+          clearable
+          autofocus
+          class=""
+          color="primary"
+        >
+          <template #append>
+            <q-icon name="mdi-magnify" />
+          </template>
+        </q-input>
+
+        <q-select
+          v-if="showColumnsSelect"
+          v-model="visibleColumns"
+          multiple
+          options-outlined
+          :display-value="$q.lang.table.columns"
+          emit-value
+          map-options
+          :options="mapColumns"
+          style="width: 150px"
+          options-selected-class="text-bold"
+        />
+
+        <slot name="top" :rows="rows" />
+
+        <q-space />
+
+        <q-btn
+          v-if="addItem"
+          color="primary"
+          padding="10px 16px"
+          :outline="outlineAdd"
+          :label="$t('common.actions.add')"
+          @click="onAddItem"
+        >
+        </q-btn>
+      </div>
+    </template>
+
+    <template #body-cell-actions="{ row }">
+      <q-td auto-width>
+        <slot name="body-cell-actions" :row="row" />
+        <q-item-section v-if="deleteFunction">
+          <q-btn
+            color="negative"
+            flat
+            dense
+            icon="mdi-delete"
+            style="width: 45px"
+            class="q-ml-auto q-mr-sm"
+            @click.prevent.stop="onDelete(row.id)"
+          />
+        </q-item-section>
+      </q-td>
+    </template>
+
+    <template #item="{ cols, row, index }">
+      <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4 col-lg-3">
+        <q-card
+          bordered
+          flat
+          class="q-pa-sm"
+          @click="onRowClick($event, row, index)"
+        >
+          <q-list dense>
+            <q-item
+              v-for="col in cols.filter((col) => col.name !== 'desc')"
+              :key="col.name"
+            >
+              <template v-if="col.name !== 'actions'">
+                <q-item-section>
+                  <q-item-label caption>{{ col.label }}</q-item-label>
+                  <q-item-label>{{ col.value }}</q-item-label>
+                </q-item-section>
+              </template>
+              <template v-else>
+                <slot name="body-cell-actions" :row="row" />
+                <q-item-section v-if="deleteFunction">
+                  <q-btn
+                    color="negative"
+                    flat
+                    dense
+                    icon="mdi-delete"
+                    style="width: 45px"
+                    class="q-ml-auto q-mr-sm"
+                    @click.prevent.stop="onDelete(col.id)"
+                  />
+                </q-item-section>
+              </template>
+            </q-item>
+          </q-list>
+        </q-card>
+      </div>
+    </template>
+
+    <template #loading>
+      <q-inner-loading showing color="primary" />
+    </template>
+
+    <template v-if="!hideNoDataLabel" #no-data>
+      <div v-if="!loading" class="q-my-md row justify-center full-width">
+        <div class="q-pa-md body2">
+          {{ $t("http.errors.no_records_found") }}
+        </div>
+      </div>
+    </template>
+
+    <template v-for="name in $slots" #[name]="data">
+      <slot :name="name" v-bind="data"></slot>
+    </template>
+  </q-table>
+</template>
+
+<script setup>
+import { ref, onMounted, toRaw, watch } from "vue";
+import { useI18n } from "vue-i18n";
+import { useRouter } from "vue-router";
+import { useQuasar } from "quasar";
+
+const emit = defineEmits(["onRowClick", "onAddItem", "noRows"]);
+
+const {
+  columns,
+  apiCall,
+  outlineAdd,
+  openItem,
+  openItemRoute,
+  addItem,
+  addItemRoute,
+  rowsPerPage,
+  showSearchField,
+  noApiCall,
+  hideNoDataLabel,
+  deleteFunction,
+} = defineProps({
+  columns: {
+    type: Array,
+    required: true,
+  },
+  apiCall: {
+    type: Function,
+    required: true,
+  },
+  outlineAdd: {
+    type: Boolean,
+    default: false,
+  },
+  openItem: {
+    type: Boolean,
+    default: false,
+  },
+  openItemRoute: {
+    type: String,
+    default: "",
+  },
+  addItem: {
+    type: Boolean,
+    default: true,
+  },
+  addItemRoute: {
+    type: String,
+    default: "",
+  },
+  rowsPerPage: {
+    type: Number,
+    default: 10,
+  },
+  showSearchField: {
+    type: Boolean,
+    default: true,
+  },
+  showColumnsSelect: {
+    type: Boolean,
+    default: true,
+  },
+  noApiCall: {
+    type: Boolean,
+    default: false,
+  },
+  hideNoDataLabel: {
+    type: Boolean,
+    default: false,
+  },
+  deleteFunction: {
+    type: Function,
+    default: null,
+  },
+});
+
+const router = useRouter();
+const { t } = useI18n();
+const $q = useQuasar();
+
+const rows = ref([]);
+const filter = ref("");
+const loading = ref(true);
+const fullscreen = ref(false);
+
+const getPaginationLabel = (from, to, last) => {
+  return `${from}-${to} de ${last}`;
+};
+
+watch(
+  () => apiCall,
+  async () => {
+    await onRequest();
+  },
+);
+
+const mapColumns = columns.reduce((accm, column) => {
+  if (!column.required) {
+    accm.push({
+      label: column.label,
+      value: column.name,
+    });
+  }
+  return accm;
+}, []);
+
+const visibleColumns = ref(mapColumns.map((column) => column.value));
+
+const onRowClick = (evt, row, index) => {
+  const item = toRaw(row);
+  if (openItem) {
+    if (openItemRoute) {
+      router.push({ name: openItemRoute, params: { id: item.id } });
+    } else {
+      emit("onRowClick", { evt, row, index });
+    }
+  }
+};
+
+const onAddItem = () => {
+  if (addItem) {
+    if (addItemRoute) {
+      router.push({ name: addItemRoute });
+    } else {
+      emit("onAddItem");
+    }
+  }
+};
+
+const onDelete = async (id) => {
+  if (deleteFunction) {
+    $q.dialog({
+      title: t("common.ui.messages.confirm_action"),
+      message: t("common.ui.messages.are_you_sure_delete"),
+      ok: {
+        color: "negative",
+        label: t("common.actions.delete"),
+      },
+      cancel: {
+        color: "primary",
+        label: t("common.actions.cancel"),
+      },
+    }).onOk(async () => {
+      loading.value = true;
+      try {
+        await deleteFunction(id);
+        await onRequest();
+      } catch (error) {
+        console.error(error);
+      } finally {
+        loading.value = false;
+      }
+    });
+  }
+};
+
+const onRequest = async () => {
+  if (noApiCall) {
+    loading.value = false;
+    return;
+  }
+  loading.value = true;
+
+  const response = await apiCall();
+  rows.value.splice(0, rows.value.length, ...response);
+
+  if (rows.value.length == 0) {
+    emit("noRows");
+  }
+  loading.value = false;
+};
+
+onMounted(async () => {
+  await onRequest({
+    filter: undefined,
+  });
+});
+
+defineExpose({
+  refresh: onRequest,
+});
+</script>
+
+<style lang="scss">
+@import "src/css/table.scss";
+</style>

+ 373 - 0
src/components/defaults/DefaultTableServerSide.vue

@@ -0,0 +1,373 @@
+# DefaultTableServerSide.vue
+<template>
+  <q-table
+    v-model:fullscreen="fullscreen"
+    v-model:pagination="pagination"
+    row-key="id"
+    flat
+    class="softpar-table q-pa-sm"
+    :pagination-label="getPaginationLabel"
+    :rows="rows"
+    :rows-per-page-label="$t('common.ui.table.rows_per_page')"
+    :columns="columns"
+    :visible-columns="visibleColumns"
+    :filter="pagination.filter"
+    :grid="$q.screen.lt.sm"
+    :loading="loading"
+    v-bind="$attrs"
+    @row-click="onRowClick"
+  >
+    <template #top>
+      <div
+        class="flex full-width justify-between items-center q-mb-md q-pl-sm"
+        style="gap: 1rem"
+      >
+        <q-input
+          v-if="showSearchField"
+          v-model="pagination.filter"
+          outlined
+          dense
+          debounce="500"
+          :placeholder="$t('common.actions.search')"
+          clearable
+          autofocus
+        >
+          <template #append>
+            <q-icon name="mdi-magnify" />
+          </template>
+        </q-input>
+
+        <q-select
+          v-if="showColumnsSelect"
+          v-model="visibleColumns"
+          class="q-ml-md"
+          multiple
+          dense
+          outlined
+          options-outlined
+          :display-value="$q.lang.table.columns"
+          emit-value
+          map-options
+          :options="mapColumns"
+          style="min-width: 150px"
+          options-selected-class="text-bold"
+        />
+
+        <q-space />
+
+        <q-btn
+          v-if="addItem"
+          color="primary"
+          padding="12px 16px"
+          :outline="outlineAdd"
+          :label="labelAdd"
+          @click="onAddItem"
+        />
+      </div>
+    </template>
+
+    <template #body-cell-actions="{ row }">
+      <q-td v-if="deleteFunction">
+        <q-item-section>
+          <q-btn
+            color="negative"
+            flat
+            dense
+            icon="mdi-delete"
+            style="width: 45px"
+            class="q-ml-auto q-mr-sm"
+            @click.prevent.stop="onDelete(row.id)"
+          />
+        </q-item-section>
+      </q-td>
+    </template>
+
+    <template v-if="!hideNoDataLabel" #no-data>
+      <div class="q-my-md row justify-center full-width">
+        <q-spinner v-if="loading" color="primary" size="30px" />
+        <div v-else class="q-pa-md body2">
+          {{ $t("http.errors.no_records_found") }}
+        </div>
+      </div>
+    </template>
+
+    <template #bottom="scope">
+      <div class="flex full-width justify-end">
+        <div class="flex items-center">
+          {{ $t("common.ui.table.rows_per_page") }}
+          <q-select
+            v-model="pagination.rowsPerPage"
+            class="q-mx-sm"
+            dense
+            borderless
+            :options="rowsPerPageOptions"
+          >
+            <template #option="selectData">
+              <q-item v-bind="selectData.itemProps">
+                <q-item-section>
+                  <q-item-label>{{
+                    selectData.opt == 0 ? $t("common.ui.misc.all") : selectData.opt
+                  }}</q-item-label>
+                </q-item-section>
+              </q-item>
+            </template>
+          </q-select>
+        </div>
+        <div class="flex items-center">
+          {{ pagination.from + "-" + pagination.to }} {{ $t("common.ui.table.of") }}
+          {{ pagination.rowsNumber }}
+        </div>
+        <div class="flex items-center">
+          <q-btn
+            icon="mdi-chevron-left"
+            color="grey-8"
+            round
+            dense
+            flat
+            :disable="scope.isFirstPage"
+            @click="prevPage"
+          />
+          <q-btn
+            icon="mdi-chevron-right"
+            color="grey-8"
+            round
+            dense
+            flat
+            :disable="scope.isLastPage"
+            @click="nextPage"
+          />
+        </div>
+      </div>
+    </template>
+
+    <template v-for="name in $slots" #[name]="data">
+      <slot :name="name" v-bind="data"></slot>
+    </template>
+  </q-table>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, toRaw, watch } from "vue";
+import { useI18n } from "vue-i18n";
+import { useRouter } from "vue-router";
+
+const emit = defineEmits([
+  "onRowClick",
+  "onAddItem",
+  "noRows",
+  "togglePrincipal",
+]);
+
+const {
+  columns,
+  apiCall,
+  outlineAdd,
+  openItem,
+  openItemRoute,
+  addItem,
+  addItemRoute,
+  rowsPerPage,
+  showSearchField,
+  hideNoDataLabel,
+  deleteFunction,
+} = defineProps({
+  columns: {
+    type: Array,
+    required: true,
+  },
+  apiCall: {
+    type: Function,
+    required: true,
+  },
+  labelAdd: {
+    type: String,
+    default: "Adicionar",
+  },
+  outlineAdd: {
+    type: Boolean,
+    default: false,
+  },
+  openItem: {
+    type: Boolean,
+    default: false,
+  },
+  openItemRoute: {
+    type: String,
+    default: "",
+  },
+  addItem: {
+    type: Boolean,
+    default: true,
+  },
+  addItemRoute: {
+    type: String,
+    default: "",
+  },
+  rowsPerPage: {
+    type: Number,
+    default: 10,
+  },
+  showColumnsSelect: {
+    type: Boolean,
+    default: false,
+  },
+  showSearchField: {
+    type: Boolean,
+    default: true,
+  },
+  noApiRoute: {
+    type: Boolean,
+    default: false,
+  },
+  hideNoDataLabel: {
+    type: Boolean,
+    default: false,
+  },
+  deleteFunction: {
+    type: Function,
+    default: null,
+  },
+});
+
+const { t } = useI18n();
+const router = useRouter();
+const rows = ref([]);
+const loading = ref(true);
+const fullscreen = ref(false);
+const rowsPerPageOptions = [10, 15, 25, 50];
+
+const pagination = ref({
+  filter: undefined,
+  page: 1,
+  rowsPerPage: rowsPerPage,
+  rowsNumber: 0,
+  from: 0,
+  to: 0,
+});
+
+const mapColumns = computed(() => {
+  return columns.reduce((accm, column) => {
+    if (!column.required) {
+      accm.push({
+        label: column.label.toUpperCase(),
+        value: column.name,
+      });
+    }
+    return accm;
+  }, []);
+});
+
+const visibleColumns = ref(mapColumns.value.map((column) => column.value));
+
+const onRowClick = (evt, row, index) => {
+  const item = toRaw(row);
+  if (openItem) {
+    if (openItemRoute) {
+      router.push({ name: openItemRoute, params: { id: item.id } });
+    } else {
+      emit("onRowClick", { evt, row, index });
+    }
+  }
+};
+
+const onAddItem = () => {
+  if (addItem) {
+    if (addItemRoute) {
+      router.push({ name: addItemRoute });
+    } else {
+      emit("onAddItem");
+    }
+  }
+};
+
+const onDelete = async (id) => {
+  if (deleteFunction) {
+    loading.value = true;
+    try {
+      await deleteFunction(id);
+      await onRequest();
+    } catch (error) {
+      console.error(error);
+    } finally {
+      loading.value = false;
+    }
+  }
+};
+
+const prevPage = () => {
+  pagination.value.page--;
+  onRequest();
+};
+
+const nextPage = () => {
+  pagination.value.page++;
+  onRequest();
+};
+
+const getPaginationLabel = (from, to, total) => {
+  return `${from}-${to} ${t?.("common.ui.table.of") ?? "of"} ${total}`;
+};
+
+let isFetching = false;
+const onRequest = async () => {
+  if (isFetching) return;
+
+  isFetching = true;
+  loading.value = true;
+
+  try {
+    const response = await apiCall({
+      page: pagination.value.page,
+      perPage: pagination.value.rowsPerPage,
+      filter: pagination.value.filter,
+    });
+
+    rows.value = response.data.result.data;
+    pagination.value.rowsNumber = response.data.result.total;
+    pagination.value.from = response.data.result.from;
+    pagination.value.to = response.data.result.to;
+
+    if (rows.value.length === 0) {
+      emit("noRows");
+    }
+  } catch (error) {
+    console.error("Error fetching data:", error);
+  } finally {
+    loading.value = false;
+    isFetching = false;
+  }
+};
+
+watch(
+  () => apiCall,
+  () => onRequest(),
+);
+
+watch(
+  pagination,
+  async (newVal, oldVal) => {
+    if (!oldVal || loading.value) return;
+
+    if (
+      newVal.rowsPerPage !== oldVal.rowsPerPage ||
+      newVal.filter !== oldVal.filter ||
+      newVal.page !== oldVal.page
+    ) {
+      await onRequest();
+    }
+  },
+  { deep: true },
+);
+
+onMounted(async () => {
+  await onRequest();
+});
+
+defineExpose({
+  refresh: onRequest,
+});
+</script>
+
+<style lang="scss">
+@import "src/css/table.scss";
+</style>

+ 32 - 0
src/components/defaults/DefaultTabs.vue

@@ -0,0 +1,32 @@
+<template>
+  <q-tabs
+    v-model="tab"
+    class="button bg-background-2 text-font"
+    indicator-color="transparent"
+    active-color="primary"
+    v-bind="$attrs"
+    align="justify"
+    active-bg-color="white"
+  >
+    <q-tab
+      v-for="(q_tab, i) in tabsItems"
+      :key="i"
+      :name="q_tab.name"
+      :label="q_tab.label"
+      :disable="q_tab.disable"
+      :class="{ hidden: q_tab.hide }"
+    />
+  </q-tabs>
+</template>
+
+<script setup>
+const { tabsItems } = defineProps({
+  tabsItems: {
+    type: Array,
+    required: false,
+    default: () => [],
+  },
+});
+
+const tab = defineModel({ type: String });
+</script>

+ 19 - 0
src/components/layout/DefaultHeaderPage.vue

@@ -0,0 +1,19 @@
+<template>
+  <div>
+    <q-breadcrumbs class="q-mb-md">
+      <q-breadcrumbs-el
+        v-for="breadcrumb in $route.meta?.breadcrumbs"
+        :key="breadcrumb?.name"
+        :label="$t(breadcrumb?.title)"
+        :to="{ name: breadcrumb?.name }"
+      />
+    </q-breadcrumbs>
+    <div class="flex items-center justify-between">
+      <span class="text-h5">{{ $t($route.meta?.title) }}</span>
+      <slot name="after" />
+    </div>
+    <q-separator class="q-my-sm" />
+  </div>
+</template>
+
+<script setup></script>

+ 340 - 0
src/components/layout/LeftMenuLayout.vue

@@ -0,0 +1,340 @@
+<template>
+  <q-drawer
+    v-bind="$attrs"
+    :model-value="true"
+    show-if-above
+    no-swipe-close
+    no-swipe-open
+    :width="250"
+    :mini-width="64"
+    :breakpoint="500"
+    :mini="miniState"
+    :behavior="'desktop'"
+    class="detached-container"
+  >
+    <div class="column full-height q-pa-sm no-wrap">
+      <div
+        v-if="!$q.screen.lt.md"
+        class="toggle-button-wrapper absolute"
+        style="top: 50%; right: -32px; z-index: 1"
+      >
+        <q-btn
+          flat
+          round
+          size="sm"
+          padding="8px 8px"
+          @click="miniState = !miniState"
+        >
+          <q-icon
+            :name="miniState ? 'mdi-chevron-right' : 'mdi-chevron-left'"
+          />
+          <q-tooltip
+            anchor="center right"
+            self="center left"
+            :offset="[10, 10]"
+            >{{
+              miniState
+                ? $t("ui.navigation.expand_menu")
+                : $t("ui.navigation.collapse_menu")
+            }}</q-tooltip
+          >
+        </q-btn>
+      </div>
+
+      <q-list class="column no-wrap">
+        <template v-for="item in navigationItems">
+          <template v-if="item.permission">
+            <q-item
+              v-if="item.type === 'single'"
+              :key="item.name"
+              v-ripple
+              clickable
+              exact-active-class="menu-selected"
+              exact
+              active-class="menu-selected"
+              :to="{ name: item.name }"
+              class="q-my-xs"
+            >
+              <q-item-section avatar>
+                <q-icon :name="item.icon" style="font-size: 18px" />
+              </q-item-section>
+              <q-item-section>{{ $t(item.title) }}</q-item-section>
+              <q-tooltip
+                v-if="miniState"
+                anchor="center right"
+                self="center left"
+                :offset="[10, 10]"
+                >{{ $t(item.title) }}</q-tooltip
+              >
+            </q-item>
+            <!-- Expansive Menu with children -->
+            <div v-else :key="item.title">
+              <template v-if="!miniState">
+                <q-tooltip
+                  v-if="miniState"
+                  anchor="center right"
+                  self="center left"
+                  :offset="[10, 10]"
+                  >{{ $t(item.title) }}</q-tooltip
+                >
+                <q-expansion-item
+                  v-model="isExpasionItemExpanded"
+                  header-class="menu-item--spaced"
+                  :class="{
+                    'menu-selected':
+                      childrenAreActive(item.children) &&
+                      !isExpasionItemExpanded,
+                  }"
+                  class="menu-item--spaced"
+                >
+                  <template #header>
+                    <q-item-section avatar>
+                      <q-icon :name="item.icon" style="font-size: 18px" />
+                    </q-item-section>
+                    <q-item-section>{{ $t(item.title) }}</q-item-section>
+                  </template>
+                  <div v-for="child in item.childrens" :key="child.name">
+                    <q-item
+                      v-ripple
+                      clickable
+                      :to="{ name: child.name }"
+                      exact
+                      exact-active-class="menu-selected"
+                      class="menu-item--spaced q-pl-lg"
+                    >
+                      <q-item-section avatar>
+                        <q-icon :name="child.icon" style="font-size: 18px" />
+                      </q-item-section>
+                      <q-item-section>{{ $t(child.title) }}</q-item-section>
+                      <q-tooltip
+                        v-if="miniState"
+                        anchor="center right"
+                        self="center left"
+                        :offset="[10, 10]"
+                        >{{ $t(child.title) }}</q-tooltip
+                      >
+                    </q-item>
+                  </div>
+                </q-expansion-item>
+              </template>
+              <template v-else>
+                <q-item
+                  v-ripple
+                  clickable
+                  exact
+                  exact-active-class="menu-selected"
+                  class="menu-item--spaced"
+                >
+                  <q-item-section avatar>
+                    <q-icon :name="item.icon" style="font-size: 18px" />
+                  </q-item-section>
+                  <q-item-section>{{ $t(item.title) }}</q-item-section>
+                  <q-tooltip
+                    v-if="miniState"
+                    anchor="center right"
+                    self="center left"
+                    :offset="[10, 10]"
+                    >{{ $t(item.title) }}</q-tooltip
+                  >
+                  <q-menu anchor="center right" self="top start">
+                    <q-list>
+                      <q-item
+                        v-for="child in item.childrens"
+                        :key="child.name"
+                        v-ripple
+                        v-close-popup
+                        clickable
+                        :to="{ name: child.name }"
+                        exact
+                        exact-active-class="menu-selected"
+                      >
+                        <q-item-section avatar>
+                          <q-icon :name="child.icon" style="font-size: 18px" />
+                        </q-item-section>
+                        <q-item-section>{{ $t(child.title) }}</q-item-section>
+                      </q-item>
+                    </q-list>
+                  </q-menu>
+                </q-item>
+              </template>
+            </div>
+          </template>
+        </template>
+      </q-list>
+      <q-list class="column q-mb-md no-wrap" style="border-radius: 6px">
+      </q-list>
+      <q-list class="q-mt-auto">
+        <q-item v-ripple clickable>
+          <div class="flex">
+            <q-item-section avatar>
+              <template #default>
+                <!-- <img
+                  :src="someAvatar()"
+                  alt="avatar"
+                  style="width: 20px; height: 20px; border-radius: 50%"
+                /> -->
+                <q-icon
+                  name="mdi-account"
+                  color="primary"
+                  style="font-size: 20px"
+                />
+              </template>
+            </q-item-section>
+            <q-item-section>{{ user_store.user.name }}</q-item-section>
+          </div>
+          <q-tooltip
+            v-if="miniState"
+            anchor="center right"
+            self="center left"
+            :offset="[10, 10]"
+            >{{ user_store.user.name }}</q-tooltip
+          >
+          <q-menu anchor="center right" self="top start">
+            <q-list class="column no-wrap overflow-hidden">
+              <q-item
+                v-ripple
+                v-close-popup
+                clickable
+                :to="{ name: 'ProfilePage' }"
+                exact
+                exact-active-class="menu-selected"
+              >
+                <div class="flex">
+                  <q-item-section avatar>
+                    <q-icon
+                      name="account_circle"
+                      color="primary"
+                      style="font-size: 18px"
+                    />
+                  </q-item-section>
+                  <q-item-section>{{
+                    $t("user.profile.singular")
+                  }}</q-item-section>
+                </div>
+              </q-item>
+              <q-item v-ripple clickable @click="logoutFn">
+                <div class="flex">
+                  <q-item-section avatar>
+                    <q-icon
+                      name="logout"
+                      color="negative"
+                      style="font-size: 18px"
+                    />
+                  </q-item-section>
+                  <q-item-section>{{ $t("auth.logout") }}</q-item-section>
+                </div>
+              </q-item>
+            </q-list>
+          </q-menu>
+        </q-item>
+        <q-item v-ripple clickable @click="openUrl('https://softpar.inf.br')">
+          <div class="flex full-width justify-center">
+            <q-img
+              :src="
+                miniState || $q.screen.lt.md
+                  ? LogoSoftparMini
+                  : $q.dark.isActive
+                    ? LogoSoftparLight
+                    : LogoSoftparDark
+              "
+              style="width: 100%; height: 30px; max-width: 114px"
+            />
+          </div>
+        </q-item>
+      </q-list>
+      <div class="full-width text-center text-subtitle3">
+        <span class="text-caption text-weight-light">{{ version }}</span>
+      </div>
+    </div>
+  </q-drawer>
+</template>
+<script setup>
+import { ref, watch, watchEffect } from "vue";
+import { useAuth } from "src/composables/useAuth";
+import { useRouter, useRoute } from "vue-router";
+import { userStore } from "src/stores/user";
+import { navigationStore } from "src/stores/navigation";
+import LogoSoftparLight from "src/assets/softpar_logo_light.svg";
+import LogoSoftparDark from "src/assets/softpar_logo_dark.svg";
+import LogoSoftparMini from "src/assets/softpar_logo_mini.svg";
+import { Cookies } from "quasar";
+import { useQuasar } from "quasar";
+
+const $q = useQuasar();
+const { logout } = useAuth();
+const router = useRouter();
+const route = useRoute();
+const user_store = userStore();
+const { navigationItems } = navigationStore();
+
+const version = "0.0.1";
+
+const miniStateCookies = Cookies.get("miniState")
+
+const miniState = ref(miniStateCookies === "true" ? true : false);
+
+const childrenAreActive = (children) => {
+  if (!children) return false;
+  return children.some((child) => {
+    return route.path.includes(child.path);
+  });
+};
+
+// const someAvatar = () => {
+//   return "https://cdn.quasar.dev/img/avatar4.jpg";
+// };
+
+const isExpasionItemExpanded = ref(false);
+
+watchEffect(() => {
+  if ($q.screen.lt.md) {
+    miniState.value = true;
+    if (Array.isArray(isExpasionItemExpanded.value)) {
+      isExpasionItemExpanded.value.forEach((expansion, index) => {
+        isExpasionItemExpanded.value[index] = false;
+      });
+    } else {
+      isExpasionItemExpanded.value = false;
+    }
+  }
+});
+
+const logoutFn = async () => {
+  await logout();
+  router.push({ name: "LoginPage" });
+};
+
+const openUrl = (url) => {
+  window.open(url, "_blank");
+};
+
+watch(miniState, () => {
+  Cookies.set("miniState", miniState.value);
+});
+</script>
+
+<style lang="scss" scoped>
+@import "/src/css/quasar.variables.scss";
+.text-subtitle3 {
+  font-size: 1.1rem !important;
+  font-weight: 400 !important;
+}
+
+.menu-selected {
+  background-color: rgba($primary, 0.1);
+  color: $primary;
+}
+
+.toggle-button-wrapper {
+  transition: transform 0.3s ease;
+}
+
+.toggle-button-wrapper:hover {
+  transform: scale(1.1);
+}
+
+.menu-item--spaced {
+  margin-top: 5px;
+  margin-bottom: 5px;
+}
+</style>

+ 128 - 0
src/components/layout/LeftMenuLayoutMobile.vue

@@ -0,0 +1,128 @@
+<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
+<template>
+  <q-drawer
+    v-bind="$attrs"
+    v-model="leftDrawerOpen"
+    :width="250"
+    :breakpoint="500"
+    behavior="mobile"
+    class="detached-container"
+  >
+    <div class="column full-height q-pa-sm no-wrap">
+      <q-list class="column no-wrap">
+        <template v-for="item in navigationItems">
+          <template v-if="item.permission">
+            <!-- Single Menu -->
+            <q-item
+              v-if="item.type === 'single'"
+              :key="item.name"
+              v-ripple
+              clickable
+              exact-active-class="menu-selected"
+              exact
+              active-class="menu-selected"
+              :to="{ name: item.name }"
+              class="q-my-xs"
+            >
+              <q-item-section avatar>
+                <q-icon :name="item.icon" style="font-size: 18px" />
+              </q-item-section>
+              <q-item-section>{{ $t(item.title) }}</q-item-section>
+            </q-item>
+            <!-- Expansive Menu with children -->
+            <q-expansion-item
+              v-else
+              :key="item.name"
+              v-model="isExpasionItemExpanded"
+              header-class="menu-item--spaced"
+              :class="{
+                'menu-selected':
+                  childrenAreActive(item.children) && !isExpasionItemExpanded,
+              }"
+            >
+              <template #header>
+                <q-item-section avatar>
+                  <q-icon :name="item.icon" style="font-size: 18px" />
+                </q-item-section>
+                <q-item-section>{{ $t(item.title) }}</q-item-section>
+              </template>
+              <div v-for="child in item.childrens" :key="child.name">
+                <q-item
+                  v-ripple
+                  clickable
+                  :to="{ name: child.name }"
+                  exact
+                  exact-active-class="menu-selected"
+                  class="menu-item--spaced q-pl-lg"
+                >
+                  <q-item-section avatar>
+                    <q-icon :name="child.icon" style="font-size: 18px" />
+                  </q-item-section>
+                  <q-item-section>{{ $t(child.title) }}</q-item-section>
+                </q-item>
+              </div>
+            </q-expansion-item>
+          </template>
+        </template>
+      </q-list>
+
+      <q-list class="q-mt-auto">
+        <q-item v-ripple clickable @click="openUrl('https://softpar.inf.br')">
+          <div class="flex full-width justify-center">
+            <q-img
+              :src="$q.dark.isActive ? LogoSoftparLight : LogoSoftparDark"
+              style="width: 100%; height: 30px; max-width: 114px"
+            />
+          </div>
+        </q-item>
+      </q-list>
+      <div class="full-width text-center text-subtitle3">
+        <span class="text-caption text-weight-light">1.0.0</span>
+      </div>
+    </div>
+  </q-drawer>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { useRoute } from "vue-router";
+import { navigationStore } from "src/stores/navigation";
+import LogoSoftparLight from "src/assets/softpar_logo_light.svg";
+import LogoSoftparDark from "src/assets/softpar_logo_dark.svg";
+const route = useRoute();
+
+const leftDrawerOpen = defineModel({ type: Boolean });
+
+const { navigationItems } = navigationStore();
+
+const childrenAreActive = (children) => {
+  if (!children) return false;
+  return children.some((child) => {
+    return route.path.includes(child.path);
+  });
+};
+
+const isExpasionItemExpanded = ref(false);
+
+const openUrl = (url) => {
+  window.open(url, "_blank");
+};
+</script>
+
+<style lang="scss" scoped>
+@import "/src/css/quasar.variables.scss";
+.text-subtitle3 {
+  font-size: 1.1rem !important;
+  font-weight: 400 !important;
+}
+
+.menu-selected {
+  background-color: rgba($primary, 0.1);
+  color: $primary;
+}
+
+.menu-item--spaced {
+  margin-top: 5px;
+  margin-bottom: 5px;
+}
+</style>

+ 168 - 0
src/components/regions/CitySelect.vue

@@ -0,0 +1,168 @@
+<template>
+  <q-select
+    v-model="selectedCity"
+    v-bind="$attrs"
+    use-input
+    hide-selected
+    fill-input
+    clearable
+    :options="cityOptions"
+    :label
+    :rules
+    :loading
+    :placeholder="$t('common.actions.search') + ' ' + $t('ui.navigation.city')"
+    :error
+    :error-message
+    @filter="filterFn"
+  >
+    <template #no-option>
+      <q-item>
+        <q-item-section class="text-grey">
+          {{ $t("http.errors.no_records_found") }}
+        </q-item-section>
+      </q-item>
+    </template>
+  </q-select>
+</template>
+
+<script setup>
+import { getCities } from "src/api/city";
+import { ref, onMounted, watch } from "vue";
+import { normalizeString } from "src/helpers/utils";
+import { useI18n } from "vue-i18n";
+
+const emit = defineEmits(["selectedStateId"]);
+
+const { state, label, rules, initialId, country } = defineProps({
+  // This country prop is here for future use, maybe
+  country: {
+    type: Object,
+    required: false,
+    default: () => {
+      return {
+        label: "Brasil",
+        value: 1,
+      };
+    },
+  },
+  state: {
+    type: Object,
+    required: false,
+    default: null,
+  },
+  label: {
+    type: String,
+    default: () => useI18n().t("ui.navigation.city"),
+  },
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  initialId: {
+    type: Number,
+    required: false,
+    default: null,
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const selectedCity = defineModel({ type: Object });
+
+const loading = ref(false);
+const baseOptions = ref([]);
+const cityOptions = ref([]);
+
+const filterFn = async (val, update) => {
+  ensureOnlyPossibleOptions(country?.value, state?.value);
+  const needle = normalizeString(val);
+  cityOptions.value = cityOptions.value.filter((v) => {
+    return (
+      normalizeString(v.label).includes(needle) ||
+      normalizeString(v.code).includes(needle)
+    );
+  });
+  update();
+};
+
+const selectCityByName = (name) => {
+  if (selectedCity.value?.label === name) {
+    return;
+  }
+  selectedCity.value = baseOptions.value.find((city) => city.label === name);
+};
+
+const selectCityById = (id) => {
+  if (selectedCity.value?.value === id) {
+    return;
+  }
+  selectedCity.value = baseOptions.value.find((city) => city.value === id);
+};
+
+const ensureOnlyPossibleOptions = (country_id, state_id) => {
+  if (state_id) {
+    cityOptions.value = baseOptions.value.filter((city) => {
+      if (country_id) {
+        return city.country_id === country_id && city.state_id === state_id;
+      }
+      return city.state_id === state_id;
+    });
+  }
+  if (!!state_id && !country_id) {
+    cityOptions.value = baseOptions.value;
+  }
+};
+
+watch(
+  () => state,
+  (value, oldValue) => {
+    if (
+      value?.value != oldValue?.value &&
+      value?.value != selectedCity.value?.state_id
+    ) {
+      selectedCity.value = null;
+    }
+    if (value) {
+      ensureOnlyPossibleOptions(country?.value, value.value);
+    }
+  },
+  { immediate: true },
+);
+
+watch(selectedCity, () => {
+  if (selectedCity.value?.state_id) {
+    emit("selectedStateId", selectedCity.value.state_id);
+  }
+});
+
+onMounted(async () => {
+  try {
+    loading.value = true;
+    const baseCities = await getCities();
+    baseOptions.value = baseCities.map((city) => ({
+      label: city.name,
+      value: city.id,
+      state_id: city.state_id,
+    }));
+    cityOptions.value = baseOptions.value;
+    if (initialId) {
+      selectCityById(initialId);
+    }
+  } catch (e) {
+    console.log(e);
+  } finally {
+    loading.value = false;
+  }
+});
+
+defineExpose({
+  selectCityByName,
+  selectCityById,
+});
+</script>

+ 117 - 0
src/components/regions/CountrySelect.vue

@@ -0,0 +1,117 @@
+<template>
+  <q-select
+    v-model="selectedCountry"
+    v-bind="$attrs"
+    use-input
+    hide-selected
+    fill-input
+    clearable
+    :options="countryOptions"
+    :label="label"
+    :loading="loading"
+    :placeholder="
+      $t('common.actions.search') + ' ' + $t('ui.navigation.country')
+    "
+    :rules="rules"
+    :error
+    :error-message
+    @filter="filterFn"
+  >
+    <template #no-option>
+      <q-item>
+        <q-item-section class="text-grey">
+          {{ $t("http.errors.no_records_found") }}
+        </q-item-section>
+      </q-item>
+    </template>
+  </q-select>
+</template>
+
+<script setup>
+import { getCountries } from "src/api/country";
+import { ref, onMounted } from "vue";
+import { useI18n } from "vue-i18n";
+
+const { label, rules, initialId } = defineProps({
+  label: {
+    type: String,
+    default: () => useI18n().t("ui.navigation.country"),
+  },
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  initialId: {
+    type: Number,
+    required: false,
+    default: null,
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const selectedCountry = defineModel({ type: Object });
+
+const loading = ref(false);
+const baseCountry = ref([]);
+const countries = ref([]);
+const countryOptions = ref([]);
+
+const filterFn = async (val, update) => {
+  const filter = () => {
+    const needle = val.toLowerCase();
+    countries.value = baseCountry.value.filter((v) =>
+      v.name.toLowerCase().includes(needle),
+    );
+    countryOptions.value = countries.value.map((country) => ({
+      label: country.name,
+      value: country.id,
+    }));
+  };
+
+  update(filter);
+};
+
+const selectCountryByName = (name) => {
+  if (selectedCountry.value && selectedCountry.value.label === name) return;
+  selectedCountry.value = countryOptions.value.find(
+    (country) => country.label === name,
+  );
+};
+
+const selectCountryById = (id) => {
+  if (selectedCountry.value && selectedCountry.value.id === id) return;
+  selectedCountry.value = countryOptions.value.find(
+    (country) => country.value === id,
+  );
+};
+
+onMounted(async () => {
+  try {
+    loading.value = true;
+    baseCountry.value = await getCountries();
+    countryOptions.value = baseCountry.value.map((country) => ({
+      label: country.name,
+      value: country.id,
+    }));
+    if (initialId) {
+      selectCountryById(initialId);
+    }
+  } catch (e) {
+    console.log(e);
+  } finally {
+    loading.value = false;
+  }
+});
+
+defineExpose({
+  selectCountryByName,
+  selectCountryById,
+});
+</script>

+ 167 - 0
src/components/regions/StateSelect.vue

@@ -0,0 +1,167 @@
+<template>
+  <q-select
+    v-model="selectedState"
+    v-bind="$attrs"
+    use-input
+    hide-selected
+    fill-input
+    clearable
+    :options="stateOptions"
+    :label
+    :loading
+    :placeholder="$t('common.actions.search') + ' ' + $t('ui.navigation.state')"
+    :rules
+    :error
+    :error-message
+    @filter="filterFn"
+  >
+    <template #no-option>
+      <q-item>
+        <q-item-section class="text-grey">
+          {{ $t("http.errors.no_records_found") }}
+        </q-item-section>
+      </q-item>
+    </template>
+  </q-select>
+</template>
+
+<script setup>
+import { getStates } from "src/api/state";
+import { ref, onMounted, watch } from "vue";
+import { normalizeString } from "src/helpers/utils";
+import { useI18n } from "vue-i18n";
+
+const emit = defineEmits(["selectedCountryId"]);
+
+const { country, label, rules, initialId } = defineProps({
+  country: {
+    type: Object,
+    required: false,
+    default: () => {
+      return {
+        label: "Brasil",
+        value: 1,
+      };
+    },
+  },
+  label: {
+    type: String,
+    default: () => useI18n().t("ui.navigation.state"),
+  },
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  initialId: {
+    type: Number,
+    required: false,
+    default: null,
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const selectedState = defineModel({ type: Object });
+
+const loading = ref(false);
+const baseOptions = ref([]);
+const stateOptions = ref([]);
+
+const filterFn = (val, update) => {
+  ensureOnlyPossibleOptions(country?.value);
+  const needle = normalizeString(val);
+  stateOptions.value = stateOptions.value.filter((v) => {
+    return (
+      normalizeString(v.label).includes(needle) ||
+      normalizeString(v.code).includes(needle)
+    );
+  });
+  update();
+};
+
+const selectStateById = async (id) => {
+  if (selectedState.value?.value === id) {
+    return;
+  }
+  selectedState.value = baseOptions.value.find((state) => state.value === id);
+};
+
+const selectStateByName = (name) => {
+  if (selectedState.value?.label === name) {
+    return;
+  }
+  selectedState.value = baseOptions.value.find((state) => state.label === name);
+};
+
+const selectStateByCode = (code) => {
+  if (selectedState.value?.code === code) {
+    return;
+  }
+  selectedState.value = baseOptions.value.find((state) => state.code === code);
+};
+
+const ensureOnlyPossibleOptions = (country_id) => {
+  if (country_id) {
+    stateOptions.value = baseOptions.value.filter(
+      (state) => state.country_id === country_id,
+    );
+  } else {
+    stateOptions.value = baseOptions.value;
+  }
+};
+
+watch(
+  () => country,
+  (value, oldValue) => {
+    if (
+      value?.value != oldValue?.value &&
+      value?.value != selectedState.value?.country_id
+    ) {
+      selectedState.value = null;
+    }
+    if (value) {
+      ensureOnlyPossibleOptions(value.value);
+    }
+  },
+  { immediate: true },
+);
+
+watch(selectedState, () => {
+  if (selectedState.value?.country_id) {
+    emit("selectedCountryId", selectedState.value.country_id);
+  }
+});
+
+onMounted(async () => {
+  try {
+    loading.value = true;
+    const baseStates = await getStates();
+    baseOptions.value = baseStates.map((state) => ({
+      label: state.name,
+      value: state.id,
+      code: state.code,
+      country_id: state.country_id,
+    }));
+    stateOptions.value = baseOptions.value;
+    if (initialId) {
+      selectStateById(initialId);
+    }
+  } catch (e) {
+    console.log(e);
+  } finally {
+    loading.value = false;
+  }
+});
+
+defineExpose({
+  selectStateById,
+  selectStateByName,
+  selectStateByCode,
+});
+</script>

+ 57 - 0
src/composables/useAuth.js

@@ -0,0 +1,57 @@
+import api from "src/api";
+import { permissionStore } from "src/stores/permission";
+import { userStore } from "src/stores/user";
+
+export const useAuth = () => {
+  const setAuthDataFromPayload = async (tokens) => {
+    const { access_token, user } = tokens;
+    userStore().user = user;
+    userStore().accessToken = access_token;
+    await permissionStore().fetchScopes();
+  };
+
+  const login = async (email, password) => {
+    try {
+      const response = await api.post("/login", {
+        email: email,
+        password: password,
+      });
+
+      if (response.status === 200) {
+        await setAuthDataFromPayload(response.data.payload);
+      }
+      return response;
+    } catch (error) {
+      return Promise.reject(error);
+    }
+  };
+
+  const logout = async () => {
+    try {
+      const response = await api.post("/logout");
+      if (response.status === 200) {
+        userStore().resetUser();
+      }
+    } catch (error) {
+      console.error(error);
+    }
+  };
+
+  const refresh = async () => {
+    try {
+      const response = await api.post("/refresh");
+      if (response.status === 200) {
+        await setAuthDataFromPayload(response.data.payload);
+      }
+      return response;
+    } catch (error) {
+      return Promise.reject(error);
+    }
+  };
+
+  return {
+    login,
+    logout,
+    refresh,
+  };
+};

+ 173 - 0
src/composables/useFormUpdateTracker.js

@@ -0,0 +1,173 @@
+import { reactive, computed, toRaw, isReactive, watch } from "vue";
+import isEqual from "fast-deep-equal";
+
+export const useFormUpdateTracker = (initialFormValue) => {
+  const form = reactive(deepClone(initialFormValue));
+  let originalForm = deepClone(initialFormValue);
+  const updatedFields = reactive({});
+
+  const getUpdatedFields = computed(() => {
+    return updatedFields;
+  });
+
+  const hasUpdatedFields = computed(() => {
+    return Object.keys(updatedFields).length > 0;
+  });
+
+  watch(
+    form,
+    (newValue) => {
+      const changes = diff(toRaw(newValue), originalForm);
+      Object.keys(updatedFields).forEach((key) => delete updatedFields[key]);
+      Object.assign(updatedFields, changes);
+    },
+    { deep: true },
+  );
+
+  const resetUpdateForm = () => {
+    const newFormState = deepClone(originalForm);
+    Object.keys(form).forEach((key) => delete form[key]);
+    Object.assign(form, newFormState);
+  };
+
+  const setUpdateFormAsOriginal = () => {
+    originalForm = deepClone(toRaw(form));
+  };
+
+  const getFormAsFormData = () => {
+    const formData = new FormData();
+    buildFormData(formData, form);
+    return formData;
+  };
+
+  const getUpdatedFieldsAsFormData = (spoofMethod = null) => {
+    const formData = new FormData();
+    buildFormData(formData, updatedFields);
+    if (spoofMethod) {
+      formData.append("_method", spoofMethod.toUpperCase());
+    }
+
+    return formData;
+  };
+
+  return {
+    form,
+    getUpdatedFields,
+    hasUpdatedFields,
+    resetUpdateForm,
+    setUpdateFormAsOriginal,
+    getFormAsFormData,
+    getUpdatedFieldsAsFormData,
+  };
+};
+
+/**
+ * A recursive function to find the differences between two objects.
+ * It returns a new object containing only the keys that have been added,
+ * changed, or removed (set to null).
+ * @param {object} currentObj The current state of the object.
+ * @param {object} baseObj The original object to compare against.
+ * @returns {object} An object with only the changed, new, or deleted keys.
+ */
+function diff(currentObj, baseObj) {
+  const changes = {};
+  const currentKeys = Object.keys(currentObj);
+  const baseKeys = Object.keys(baseObj);
+
+  for (const key of currentKeys) {
+    const currentValue = currentObj[key];
+    const baseValue = baseObj[key];
+
+    if (!isEqual(currentValue, baseValue)) {
+      if (
+        currentValue &&
+        typeof currentValue === "object" &&
+        !Array.isArray(currentValue) &&
+        baseValue &&
+        typeof baseValue === "object" &&
+        !Array.isArray(baseValue)
+      ) {
+        const nestedChanges = diff(currentValue, baseValue);
+        if (Object.keys(nestedChanges).length > 0) {
+          changes[key] = nestedChanges;
+        }
+      } else {
+        changes[key] = deepClone(currentValue);
+      }
+    }
+  }
+
+  for (const key of baseKeys) {
+    if (!Object.prototype.hasOwnProperty.call(currentObj, key)) {
+      changes[key] = null;
+    }
+  }
+
+  return changes;
+}
+
+/**
+ * Recursively builds a FormData object from a nested plain object using bracket notation
+ * for keys, which is compatible with PHP/Laravel backends.
+ * @param {FormData} formData The FormData instance.
+ * @param {object} data The plain object to serialize.
+ * @param {string} parentKey The base key for nested properties.
+ */
+function buildFormData(formData, data, parentKey = "") {
+  if (data === undefined) {
+    return;
+  }
+
+  if (data == null) {
+    formData.append(parentKey, null);
+    return;
+  }
+
+  if (Array.isArray(data)) {
+    data.forEach((value, index) => {
+      buildFormData(formData, value, `${parentKey}[${index}]`);
+    });
+    return;
+  }
+
+  if (
+    typeof data === "object" &&
+    !(data instanceof File) &&
+    !(data instanceof Date)
+  ) {
+    Object.keys(data).forEach((key) => {
+      const propName = parentKey ? `${parentKey}[${key}]` : key;
+      buildFormData(formData, data[key], propName);
+    });
+    return;
+  }
+
+  let valueToAppend = data;
+  if (data instanceof Date) {
+    valueToAppend = data.toISOString().slice(0, 19).replace("T", " ");
+  }
+
+  formData.append(parentKey, valueToAppend);
+}
+
+/** * Deep clones an object using structuredClone if available,
+ * otherwise falls back to JSON methods.
+ * If the object is reactive, it converts it to a raw object first.
+ * @param {object} obj The object to clone.
+ * @returns {object} A deep clone of the input object.
+ */
+function deepClone(obj) {
+  if (obj && isReactive(obj)) {
+    obj = toRaw(obj);
+  }
+  if (typeof structuredClone === "function") {
+    try {
+      return structuredClone(obj);
+    } catch (e) {
+      console.warn(
+        "structuredClone not supported, using JSON methods instead: " + e,
+      );
+    }
+  }
+  return JSON.parse(JSON.stringify(obj));
+}

+ 111 - 0
src/composables/useInputRules.js

@@ -0,0 +1,111 @@
+import { useI18n } from "vue-i18n";
+
+export const useInputRules = () => {
+  const { t } = useI18n();
+
+  const emailPattern =
+    /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+  const passwordPattern = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;
+  const cepPattern = /^[0-9]{5}-[0-9]{3}$/;
+
+  const inputRules = {
+    required: (value) => !!value || t("validation.rules.required"),
+    requiredNumber: (value) => !isNaN(value) || t("validation.rules.required"),
+    requiredHideMessage: (value) => !!value,
+    min: (min) => (value) =>
+      value.length >= min ||
+      `${t("validation.rules.min")} ${min} ${t("validation.rules.characters")}`,
+    max: (max) => (value) =>
+      value.length <= max ||
+      `${t("validation.rules.max")} ${max} ${t("validation.rules.characters")}`,
+    minValue: (min) => (value) =>
+      value >= min || `${t("validation.rules.min")} ${min}`,
+    maxValue: (max) => (value) =>
+      value <= max || `${t("validation.rules.max")} ${max}`,
+    email: (value) =>
+      !value || emailPattern.test(value) || t("validation.rules.email"),
+    emails: (value) => {
+      if (!value) return true;
+      const emails = value.split(";").map((email) => email.trim());
+      return (
+        emails.every((email) => inputRules.email(email) === true) ||
+        t("validation.rules.email")
+      );
+    },
+    cpf: (value) => !value || isValidCPF(value) || t("validation.rules.cpf"),
+    cnpj: (value) => !value || isValidCNPJ(value) || t("validation.rules.cnpj"),
+    samePassword: (otherValue) => (value) =>
+      value === otherValue || t("validation.rules.same_password"),
+    password: (value) =>
+      !value || passwordPattern.test(value) || t("validation.rules.password"),
+    cep: (value) => {
+      if (!value) return true;
+      return cepPattern.test(value) || t("validation.rules.cep");
+    },
+    notSameDocument: (allDocuments) => (value) => {
+      if (!value) return true;
+      let found = 0;
+      for (const doc of allDocuments) {
+        if (doc == value) {
+          found++;
+        }
+        if (found > 1) {
+          return t("validation.rules.not_same_document");
+        }
+      }
+      return true;
+    },
+  };
+
+  return {
+    inputRules,
+  };
+};
+
+function isValidCPF(cpf) {
+  console.log("isValidCPF", cpf);
+  if (!cpf) return false;
+  cpf = cpf.replace(/[^\d]+/g, "");
+  if (cpf.length !== 11) return false;
+  if (/^(\d)\1+$/.test(cpf)) return false;
+  let sum = 0;
+  for (let i = 0; i < 9; i++) sum += parseInt(cpf.charAt(i)) * (10 - i);
+  let rev = 11 - (sum % 11);
+  if (rev === 10 || rev === 11) rev = 0;
+  if (rev !== parseInt(cpf.charAt(9))) return false;
+  sum = 0;
+  for (let i = 0; i < 10; i++) sum += parseInt(cpf.charAt(i)) * (11 - i);
+  rev = 11 - (sum % 11);
+  if (rev === 10 || rev === 11) rev = 0;
+  if (rev !== parseInt(cpf.charAt(10))) return false;
+  return true;
+}
+
+function isValidCNPJ(cnpj) {
+  if (!cnpj) return false;
+  cnpj = cnpj.replace(/[^\d]+/g, "");
+  if (cnpj.length !== 14) return false;
+  if (/^(\d)\1+$/.test(cnpj)) return false;
+  let length = cnpj.length - 2;
+  let numbers = cnpj.substring(0, length);
+  let digits = cnpj.substring(length);
+  let sum = 0;
+  let pos = length - 7;
+  for (let i = length; i >= 1; i--) {
+    sum += parseInt(numbers.charAt(length - i)) * pos--;
+    if (pos < 2) pos = 9;
+  }
+  let result = sum % 11 < 2 ? 0 : 11 - (sum % 11);
+  if (result !== parseInt(digits.charAt(0))) return false;
+  length = length + 1;
+  numbers = cnpj.substring(0, length);
+  sum = 0;
+  pos = length - 7;
+  for (let i = length; i >= 1; i--) {
+    sum += parseInt(numbers.charAt(length - i)) * pos--;
+    if (pos < 2) pos = 9;
+  }
+  result = sum % 11 < 2 ? 0 : 11 - (sum % 11);
+  if (result !== parseInt(digits.charAt(1))) return false;
+  return true;
+}

+ 81 - 0
src/composables/useScroll.js

@@ -0,0 +1,81 @@
+import { unref } from "vue";
+
+export const useScroll = () => {
+  /**
+   * Scrolls to a target element within a scroll container.
+   * The function is agnostic to whether the container is a QScrollArea or a native DOM element.
+   *
+   * @param {Ref<Component|HTMLElement>} targetRef - Ref to the target component or DOM element.
+   * @param {Ref<Component|HTMLElement>} containerRef - Ref to the container (QScrollArea or a scrollable DOM element like body).
+   * @param {Object} options - Scroll options.
+   * @param {number} options.offset - Offset from the top in pixels (default: 50).
+   * @param {number} options.duration - Animation duration for QScrollArea (default: 150). Note: duration is not supported for native scrolling.
+   * @returns {boolean} - Whether the scroll was successful.
+   */
+  const scrollToComponent = (targetRef, containerRef, options = {}) => {
+    const { offset = 150, duration = 150 } = options;
+
+    const targetInstance = unref(targetRef);
+    const containerInstance = unref(containerRef);
+
+    const targetElement = targetInstance?.$el ?? targetInstance;
+
+    const containerElement = containerInstance?.$el ?? containerInstance;
+
+    if (!targetElement || !containerElement) {
+      console.warn("useScroll: Target or container element not found");
+      return false;
+    }
+
+    try {
+      let currentElement = targetElement;
+      let offsetTop = 0;
+
+      while (currentElement && currentElement !== containerElement) {
+        offsetTop += currentElement.offsetTop;
+        if (
+          containerElement.contains(currentElement.offsetParent) ||
+          containerElement === document.body
+        ) {
+          currentElement = currentElement.offsetParent;
+        } else {
+          currentElement = null;
+        }
+      }
+
+      if (
+        containerElement !== document.body &&
+        !containerElement.contains(targetElement)
+      ) {
+        console.warn("useScroll: Target is not a child of the container");
+        return false;
+      }
+
+      const targetPosition = Math.max(0, offsetTop - offset);
+
+      if (typeof containerInstance.setScrollPosition === "function") {
+        containerInstance.setScrollPosition(
+          "vertical",
+          targetPosition,
+          duration,
+        );
+      } else {
+        const scrollable =
+          containerElement === document.body ? window : containerElement;
+        scrollable.scrollTo({
+          top: targetPosition,
+          behavior: "smooth",
+        });
+      }
+
+      return true;
+    } catch (error) {
+      console.error("useScroll: Error while scrolling", error);
+      return false;
+    }
+  };
+
+  return {
+    scrollToComponent,
+  };
+};

+ 94 - 0
src/composables/useSubmitHandler.js

@@ -0,0 +1,94 @@
+import { ref, nextTick } from "vue";
+
+export function useSubmitHandler(options = {}) {
+  const { onSuccess, onError, formRef, scrollFn, containerRef } = options;
+
+  const loading = ref(false);
+  const validationErrors = ref({});
+
+  const getFormRefs = () => {
+    const refs = formRef?.value;
+    if (!refs) return [];
+    return Array.isArray(refs) ? refs : [refs];
+  };
+
+  const scrollToFirstError = async () => {
+    if (!formRef?.value || !scrollFn) return;
+    await nextTick();
+
+    const refsToSearch = getFormRefs();
+
+    for (const ref of refsToSearch) {
+      if (!ref) continue;
+      const components = ref.getValidationComponents();
+      if (!components) continue;
+
+      const firstErrorComponent = components.find((c) => c.hasError);
+
+      if (firstErrorComponent) {
+        const container = containerRef?.value || containerRef;
+        firstErrorComponent.focus();
+        scrollFn(firstErrorComponent, container);
+        return;
+      }
+    }
+  };
+
+  const execute = async (apiCallThunk) => {
+    loading.value = true;
+    validationErrors.value = {};
+
+    let allValid = true;
+    const refsToValidate = getFormRefs();
+
+    if (refsToValidate.length > 0) {
+      for (const ref of refsToValidate) {
+        if (ref) {
+          const success = await ref.validate(true);
+          if (!success) {
+            allValid = false;
+          }
+        }
+      }
+    }
+
+    if (!allValid) {
+      loading.value = false;
+      await scrollToFirstError();
+      throw new Error("Frontend validation failed.");
+    }
+
+    try {
+      const response = await apiCallThunk();
+      if (typeof onSuccess === "function") await onSuccess(response);
+    } catch (error) {
+      await handleError(error);
+    } finally {
+      loading.value = false;
+    }
+  };
+
+  const handleError = async (error) => {
+    if (error?.response?.status === 422) {
+      const errors = error.response.data.errors || {};
+      for (const key in errors) {
+        const message = errors[key][0];
+        validationErrors.value[key] = message;
+      }
+      await scrollToFirstError();
+    }
+
+    await nextTick();
+    if (typeof onError === "function") {
+      await onError(error);
+    } else {
+      throw error;
+    }
+  };
+
+  return {
+    loading,
+    validationErrors,
+    execute,
+  };
+}

+ 92 - 0
src/css/app.scss

@@ -0,0 +1,92 @@
+@use "sass:map";
+@use "src/css/quasar.variables.scss";
+
+.flex-grow {
+  flex-grow: 1;
+  flex-shrink: 1;
+  flex-basis: 0;
+}
+
+.round {
+  border-radius: 50%;
+}
+
+.self-center {
+  align-self: center;
+}
+
+.input-disable {
+  .q-field--outlined .q-field__control::before {
+    border: 1px solid #b9b9b9 !important;
+    transition: border-color 0.36s cubic-bezier(0.4, 0, 0.2, 1);
+  }
+}
+
+.q-toolbar {
+  position: relative;
+  padding: 0 12px;
+  min-height: 50px;
+  width: auto;
+}
+
+.q-drawer:has(.detached-container) {
+  margin: 16px !important;
+  margin-bottom: 16px !important;
+  margin-left: 10px !important;
+  border-radius: 6px !important;
+  transition: all;
+}
+
+body.body--light {
+  .q-drawer:has(.detached-container) {
+    background: #{map.get($colors, "surface")} !important;
+  }
+
+  .q-menu {
+    background: #{map.get($colors, "surface")};
+  }
+
+  background: #{map.get($colors, "page")} !important;
+}
+
+body.body--dark {
+  .q-drawer:has(.detached-container) {
+    background: #{map.get($colors-dark, "surface")} !important;
+  }
+
+  .q-menu {
+    background: #{map.get($colors-dark, "surface")};
+  }
+
+  background: #{map.get($colors-dark, "page")};
+}
+
+.q-card__actions .q-btn {
+  padding: 10px 16px;
+}
+
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  margin: 0;
+}
+
+.q-separator {
+  padding-top: 0px;
+  padding-left: 0;
+}
+
+.q-scrollarea__content {
+  display: flex;
+  flex-direction: column;
+  flex: 1 1 auto !important;
+}
+
+.remove-header-expansion-item {
+  .q-list--dense > .q-item,
+  .q-item--dense {
+    display: none;
+  }
+}

+ 167 - 0
src/css/quasar.variables.scss

@@ -0,0 +1,167 @@
+// Quasar SCSS Variables with Material Design Color System
+// --------------------------------------------------
+
+// Primary Theme Colors
+$primary: #1976d2; // Material Blue 700
+$secondary: #9c27b0; // Material Purple 500
+$accent: #e91e63; // Material Pink 500
+
+// Dark Theme Base Colors
+$dark: #1d1d1d;
+
+// Status Colors
+$positive: #2e7d32; // Material Green 800
+$negative: #d32f2f; // Material Red 700
+$info: #0288d1; // Material Light Blue 700
+$warning: #ed6c02; // Material Orange 800
+
+// Extended Color System with Light/Dark Variants
+$colors: (
+  // Primary Colors and Variants
+  "primary": #1976d2,
+  // Base - Blue 700
+  "primary-light": #42a5f5,
+  // Light - Blue 400
+  "primary-dark": #1565c0,
+
+  // Dark - Blue 800
+  // Secondary Colors and Variants
+  "secondary": #9c27b0,
+  // Base - Purple 500
+  "secondary-light": #ba68c8,
+  // Light - Purple 300
+  "secondary-dark": #7b1fa2,
+
+  // Terceary Colors and Variants
+  "terciary": #ff9800,
+  // Base - Orange 500
+  "terciary-light": #ffb74d,
+  // Light - Orange 300
+  "terciary-dark": #f57c00,
+
+  // Dark - Purple 700
+  // Background Colors
+  "page": #f1f1f1,
+
+  // Surface Colors
+  "surface": #ffffff,
+  "surface-light": #f5f5f5,
+  "surface-dark": #f1f1f1,
+
+  //text color
+  "text": #000000,
+
+  // Status Colors with Variants
+  "success": #2e7d32,
+  // Green 800
+  "success-light": #4caf50,
+  // Green 500
+  "success-dark": #1b5e20,
+
+  // Green 900
+  "error": #d32f2f,
+  // Red 700
+  "error-light": #ef5350,
+  // Red 400
+  "error-dark": #c62828,
+
+  // Red 800
+  "warning": #ed6c02,
+  // Orange 800
+  "warning-light": #ff9800,
+  // Orange 500
+  "warning-dark": #e65100,
+
+  // Orange 900
+  "info": #0288d1,
+  // Light Blue 700
+  "info-light": #03a9f4,
+  // Light Blue 500
+  "info-dark": #01579b
+);
+
+// Dark Theme Color Overrides
+$colors-dark: (
+  // Primary Colors and Variants
+  "primary": #1976d2,
+  // Base - Blue 700
+  "primary-light": #42a5f5,
+  // Blue 50
+  "primary-dark": #1565c0,
+
+  "dark": #1d1d1d,
+
+  // Blue 400
+  // Secondary Colors - Lighter in Dark Mode
+  "secondary": #ce93d8,
+  // Purple 200
+  "secondary-light": #f3e5f5,
+  // Purple 50
+  "secondary-dark": #ab47bc,
+
+  //Terceary Colors - Lighter in Dark Mode
+  "terciary": #ffd191,
+  // Yellow 200
+  "terciary-light": #ffecb3,
+  // Yellow 50
+  "terciary-dark": #ffab40,
+
+  "page": #121212,
+
+  "surface": #1d1d1d,
+  "surface-light": #333333,
+  "surface-dark": #121212,
+
+  "text": #ffffff,
+
+  // Black Background
+  // Status Colors - Adjusted for Dark Mode
+  "success": #66bb6a,
+  // Green 400
+  "success-light": #81c784,
+  // Green 300
+  "success-dark": #388e3c,
+
+  // Green 700
+  "error": #f44336,
+  // Red 500
+  "error-light": #e57373,
+  // Red 300
+  "error-dark": #d32f2f,
+
+  // Red 700
+  "warning": #ffa726,
+  // Orange 400
+  "warning-light": #ffb74d,
+  // Orange 300
+  "warning-dark": #f57c00,
+
+  // Orange 700
+  "info": #29b6f6,
+  // Light Blue 400
+  "info-light": #4fc3f7,
+  // Light Blue 300
+  "info-dark": #0288d1 // Light Blue 700
+);
+
+// Generate color utility classes for light theme
+@each $name, $color in $colors {
+  .text-#{$name} {
+    color: $color !important;
+  }
+  .bg-#{$name} {
+    background: $color !important;
+  }
+}
+
+// Generate color utility classes for dark theme
+.body--dark {
+  @each $name, $color in $colors-dark {
+    .text-#{$name} {
+      color: $color !important;
+    }
+    .bg-#{$name} {
+      background: $color !important;
+    }
+  }
+}

+ 198 - 0
src/css/table.scss

@@ -0,0 +1,198 @@
+@use "sass:map";
+@use "src/css/quasar.variables.scss";
+.softpar-table {
+  padding-left: 16px !important;
+  padding-right: 16px !important;
+
+  .body--dark & {
+    --table-bg-color: #{map.get($colors-dark, "surface")}; // Using our dark background
+    --table-border-color: #{map.get($colors-dark, "surface-light")}; // Darker border
+    --table-header-color: #{map.get($colors-dark, "text")}; // Light text for dark mode
+  }
+
+  .body--light & {
+    --table-bg-color: #{map.get($colors, "surface")}; // Light background
+    --table-border-color: #{map.get($colors, "surface-light")}; // Border color
+    --table-header-color: #{map.get($colors, "text")}; // Dark text for light mode
+  }
+
+  :deep(.q-table) {
+    thead tr:first-child th {
+      background-color: $primary !important;
+    }
+
+    thead tr th {
+      text-transform: uppercase;
+      position: sticky;
+      z-index: 1;
+    }
+
+    thead tr:first-child th {
+      top: 0;
+    }
+
+    &.q-table--loading thead tr:last-child th {
+      top: 48px;
+    }
+  }
+
+  tr {
+    background-color: var(--table-bg-color) !important;
+  }
+
+  .q-table__middle {
+    border: 0.5px solid var(--table-border-color);
+    box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.15);
+    border-radius: 4px;
+  }
+
+  .q-table__top {
+    padding-top: 16px;
+    padding-left: 0px;
+    padding-right: 0px;
+    padding-bottom: 16px;
+  }
+
+  .q-table th {
+    font-weight: normal;
+    font-family: "Roboto";
+    font-style: normal;
+    font-weight: 500;
+    font-size: 14px;
+    line-height: 16px;
+    letter-spacing: 0.1px;
+    color: var(--table-header-color);
+  }
+}
+
+.ativo {
+  justify-content: center;
+  padding: 5px 12px;
+  gap: 10px;
+  border-radius: 24px;
+  width: fit-content;
+  margin-right: auto;
+  margin-left: auto;
+
+  .body--dark & {
+    background: none;
+    border: 1px solid #{map.get($colors, "positive-1")};
+  }
+
+  .body--light & {
+    background: #{map.get($colors, "positive-1")};
+    border: none;
+  }
+}
+
+.inativo {
+  justify-content: center;
+  padding: 5px 12px;
+  gap: 10px;
+  border-radius: 24px;
+  width: fit-content;
+  margin-right: auto;
+  margin-left: auto;
+
+  .body--dark & {
+    background: none;
+    border: 1px solid #{map.get($colors, "negative-1")};
+  }
+
+  .body--light & {
+    background: #{map.get($colors, "negative-1")};
+    border: none;
+  }
+}
+
+.rejeitado {
+  justify-content: center;
+  padding: 5px 12px;
+  gap: 10px;
+  border-radius: 24px;
+  width: fit-content;
+  margin-right: auto;
+  margin-left: auto;
+
+  .body--dark & {
+    background: none;
+    border: 1px solid #{map.get($colors, "negative-1")};
+  }
+
+  .body--light & {
+    background: #{map.get($colors, "negative-1")};
+    border: none;
+  }
+}
+
+.gerado {
+  justify-content: center;
+  padding: 5px 12px;
+  gap: 10px;
+  border-radius: 24px;
+  width: fit-content;
+  margin-right: auto;
+  margin-left: auto;
+
+  .body--dark & {
+    background: none;
+    border: 1px solid #{map.get($colors, "primary-1")};
+  }
+
+  .body--light & {
+    background: #{map.get($colors, "primary-1")};
+    border: none;
+  }
+}
+
+.pendente {
+  justify-content: center;
+  padding: 5px 12px;
+  gap: 10px;
+  border-radius: 24px;
+  width: fit-content;
+  margin-right: auto;
+  margin-left: auto;
+
+  .body--dark & {
+    background: none;
+    border: 1px solid #{map.get($colors, "warning")};
+  }
+
+  .body--light & {
+    background: #{map.get($colors, "warning")};
+    border: none;
+  }
+}
+
+.table-bottom {
+  .q-table__top {
+    .body--light & {
+      background: #{map.get($colors, "page")};
+    }
+
+    .body--dark & {
+      background: #{map.get($colors, "background-3")};
+    }
+  }
+
+  .q-table__bottom {
+    .body--light & {
+      background: #{map.get($colors, "page")};
+    }
+
+    .body--dark & {
+      background: #{map.get($colors, "background-3")};
+    }
+  }
+}
+
+.q-table__grid-item-card {
+  .body--light & {
+    background: #{map.get($colors, "dark")};
+  }
+
+  .body--dark & {
+    background: #{map.get($colors, "background-3")};
+  }
+}

+ 59 - 0
src/helpers/convertBase64Image.js

@@ -0,0 +1,59 @@
+const base64ToJPEG = (base64String, fileName) => {
+  // Remova a parte inicial "data:image/jpeg;base64,"
+  const base64WithoutHeader = base64String.replace(
+    /^data:image\/jpeg;base64,/,
+    ""
+  );
+
+  // Converte a string base64 para um array de bytes
+  const byteCharacters = atob(base64WithoutHeader);
+  const byteNumbers = new Array(byteCharacters.length);
+  for (let i = 0; i < byteCharacters.length; i++) {
+    byteNumbers[i] = byteCharacters.charCodeAt(i);
+  }
+  const byteArray = new Uint8Array(byteNumbers);
+
+  // Cria um objeto Blob contendo os dados da imagem
+  const blob = new Blob([byteArray], { type: "image/png" });
+
+  // Cria um link para download
+  const link = document.createElement("a");
+  link.href = URL.createObjectURL(blob);
+  link.download = fileName || "image.png";
+
+  // Adiciona o link ao documento, clica nele e remove-o
+  document.body.appendChild(link);
+  link.click();
+  document.body.removeChild(link);
+};
+
+const base64ToPNG = (base64String, fileName) => {
+  // Remova a parte inicial "data:image/jpeg;base64,"
+  const base64WithoutHeader = base64String.replace(
+    /^data:image\/png;base64,/,
+    ""
+  );
+
+  // Converte a string base64 para um array de bytes
+  const byteCharacters = atob(base64WithoutHeader);
+  const byteNumbers = new Array(byteCharacters.length);
+  for (let i = 0; i < byteCharacters.length; i++) {
+    byteNumbers[i] = byteCharacters.charCodeAt(i);
+  }
+  const byteArray = new Uint8Array(byteNumbers);
+
+  // Cria um objeto Blob contendo os dados da imagem
+  const blob = new Blob([byteArray], { type: "image/png" });
+
+  // Cria um link para download
+  const link = document.createElement("a");
+  link.href = URL.createObjectURL(blob);
+  link.download = fileName || "image.png";
+
+  // Adiciona o link ao documento, clica nele e remove-o
+  document.body.appendChild(link);
+  link.click();
+  document.body.removeChild(link);
+}
+
+export { base64ToJPEG, base64ToPNG };

+ 29 - 0
src/helpers/masks.js

@@ -0,0 +1,29 @@
+// # -> number
+// S -> letra (case insensitive)
+// N -> alphanumerico (case insensitive)
+// A -> letra (maiuscula)
+// a -> letra (minuscula)
+// X -> alphanumerico (maiusculo)
+// x -> alphanumerico (minusculo)
+const masks = {
+  Brasil: {
+    cpf: "###.###.###-##",
+    docEmpresa: "##.###.###/####-##",
+    celular: "(##) # ####-####",
+    telefone: "(##) ####-####",
+    cep: "#####-###",
+    cnpj: "##.###.###/####-##",
+    date: "##/##/####",
+    datetime: "##/##/#### ##:##",
+  },
+  Paraguay: {
+    celular: "(###) ###-###",
+    telefone: "## ### ###",
+    docEmpresa: "#######-#",
+  },
+  placasVeiculo: {
+    Brasil: "AAA-#X##",
+  }
+};
+
+export default masks;

+ 112 - 0
src/helpers/utils.js

@@ -0,0 +1,112 @@
+import { useI18n } from "vue-i18n";
+
+/**
+ * @description Corta uma string em um determinado tamanho.
+ * @param {string} string string a ser cortada.
+ * @param {string} size tamanho da string.
+ * @returns {string} string cortada.
+ */
+const excerpt = (string, size = 30) => {
+  if (size == null) return string;
+  if (string.length > size) {
+    string = string.substring(0, size) + "...";
+  }
+  return string;
+};
+
+/**
+ * @description Formata uma data de DD/MM/YYYY para YYYY-MM-DD
+ * @param {string} date data.
+ * @param {string} time tempo.
+ * @throws {Error} Caso a data seja nula ou invalida.
+ * @returns {string} data formatada.
+ */
+const formatDateDMYtoYMD = (date, time) => {
+  if (!date) throw new Error(useI18n().t("validation.rules.required"));
+  const testDate =
+    /^([0-2][0-9]|(3)[0-1])(\/)(((0)[0-9])|((1)[0-2]))(\/)\d{4}$/;
+  if (testDate.test(date) === false)
+    throw new Error(useI18n().t("validation.rules.date"));
+
+  const [day, month, year] = date.split("/");
+  return `${year}-${month}-${day} ${time ? time : ""}`;
+};
+
+/**
+ * @description Converte uma data e hora para o formato brasileiro.
+ * @param {string} dateTimeString data e hora.
+ * @returns {string} data e hora no formato brasileiro.
+ * @throws {Error} Caso a data seja nula ou invalida.
+ * @returns {string} data formatada.
+ * @example
+ * // convertDateTime("2023-05-23T13:07:27.000000Z");
+ * // Output: 23/05/2023 10:07:27
+ */
+const convertDateTime = (dateTimeString) => {
+  const dateTime = new Date(dateTimeString);
+  const options = {
+    timeZone: "America/Sao_Paulo",
+    day: "2-digit",
+    month: "2-digit",
+    year: "numeric",
+    hour: "2-digit",
+    minute: "2-digit",
+    second: "2-digit",
+  };
+  const formattedDateTime = dateTime
+    .toLocaleString("pt-BR", options)
+    .replace(",", "");
+  return formattedDateTime;
+};
+
+/**
+ * @description Formata uma data de YYYY-MM-DD para DD/MM/YYYY
+ * @param {string} dateTime data e hora.
+ * @returns {string} data e hora no formato brasileiro.
+ * @example
+ * // formatDateYMDtoDMY("2023-05-23T13:07:27.000000Z");
+ * // Output: 23/05/2023 10:07:27
+ */
+const formatDateYMDtoDMY = (dateTime) => {
+  const [datePart, timePart] = dateTime.split(" ");
+  const [year, month, day] = datePart.split("-");
+  const formattedDate = `${day}/${month}/${year}`;
+  if (timePart) {
+    const [hours, minutes, seconds] = timePart.split(":");
+    const formattedTime = `${hours}:${minutes}:${seconds}`;
+    return `${formattedDate} ${formattedTime}`;
+  }
+  return formattedDate;
+};
+
+/**
+ * @description Formata a moeda.
+ * @param {number} value valor.
+ * @returns {string} valor formatado.
+ */
+const formatToBRLCurrency = (value) => {
+  if (value != null) {
+    value = parseFloat(value);
+    return value.toLocaleString("pt-BR", {
+      minimumFractionDigits: 2,
+      style: "currency",
+      currency: "BRL",
+    });
+  }
+  return value;
+};
+
+const normalizeString = (val) =>
+  val
+    .toLowerCase()
+    .normalize("NFKD")
+    .replace(/[\u0300-\u036f~]/g, "");
+
+export {
+  formatDateDMYtoYMD,
+  formatDateYMDtoDMY,
+  excerpt,
+  convertDateTime,
+  formatToBRLCurrency,
+  normalizeString,
+};

+ 33 - 0
src/i18n/index.js

@@ -0,0 +1,33 @@
+import en from "./locales/en.json";
+import pt from "./locales/pt.json";
+import es from "./locales/es.json";
+
+export default {
+  "en": en,
+  "en-us": en,
+  "en-gb": en,
+  "en-US": en,
+  "en-GB": en,
+  "EN": en,
+  "EN-US": en,
+  "EN-GB": en,
+  "pt": pt,
+  "pt-br": pt,
+  "pt-pt": pt,
+  "pt-BR": pt,
+  "pt-PT": pt,
+  "PT": pt,
+  "PT-BR": pt,
+  "PT-PT": pt,
+  "es": es,
+  "es-es": es,
+  "es-mx": es,
+  "es-ar": es,
+  "es-ES": es,
+  "es-MX": es,
+  "es-AR": es,
+  "ES": es,
+  "ES-ES": es,
+  "ES-MX": es,
+  "ES-AR": es,
+};

+ 335 - 0
src/i18n/locales/en.json

@@ -0,0 +1,335 @@
+{
+  "common": {
+    "actions": {
+      "save": "Save",
+      "cancel": "Cancel",
+      "edit": "Edit",
+      "add": "Add",
+      "search": "Search",
+      "delete": "Delete",
+      "view": "View",
+      "back": "Back",
+      "next": "Next",
+      "resend_email": "Resend email",
+      "download_certificate": "Download certificate",
+      "download_boleto": "Download Boleto",
+      "copy_paste_code": "Copy and paste the code below to make the payment"
+    },
+    "terms": {
+      "name": "Name",
+      "email": "Email",
+      "password": "Password",
+      "description": "Description",
+      "date": "Date",
+      "start_date": "Start Date",
+      "end_date": "End Date",
+      "code": "Code",
+      "title": "Title",
+      "status": "Status",
+      "price": "Price",
+      "quantity": "Quantity",
+      "city": "City",
+      "state": "State",
+      "country": "Country",
+      "address": "Address",
+      "address_number": "Address Number",
+      "complement": "Complement",
+      "postal_code": "Postal Code",
+      "phone": "Phone",
+      "document": "Document",
+      "document_type": "Document Type",
+      "cpf": "CPF",
+      "cnpj": "CNPJ",
+      "cep": "ZIP Code",
+      "order_number": "Order Number",
+      "order_amount": "Order Amount",
+      "total_amount": "Total Amount",
+      "payment": "Payment",
+      "payment_method": "Payment Method",
+      "payment_date": "Payment Date",
+      "payment_amount": "Payment Amount",
+      "language": "Language",
+      "currency": "Currency",
+      "interests": "Interests",
+      "avatar": "Avatar",
+      "banner": "Banner",
+      "logo": "Logo",
+      "media": "Media",
+      "month": "Month",
+      "week": "Week",
+      "day": "Day",
+      "hour": "Hour",
+      "minute": "Minute",
+      "second": "Second",
+      "year": "Year",
+      "all": "All",
+      "certificate": "Certificate",
+      "version": "Version"
+    },
+    "months": {
+      "january": "January",
+      "february": "February",
+      "march": "March",
+      "april": "April",
+      "may": "May",
+      "june": "June",
+      "july": "July",
+      "august": "August",
+      "september": "September",
+      "october": "October",
+      "november": "November",
+      "december": "December"
+    },
+    "status": {
+      "active": "Active",
+      "inactive": "Inactive",
+      "canceled": "Canceled",
+      "loading": "Please wait...",
+      "yes": "Yes",
+      "no": "No"
+    },
+    "ui": {
+      "file": {
+        "choose": "Choose a file",
+        "click_select": "Click to select a file",
+        "click_select_image": "Click to select an image",
+        "drag": "Drag",
+        "drag_and_drop": "Drag and drop the file here",
+        "drag_here": "Drag the file here",
+        "selected": "File selected"
+      },
+      "table": {
+        "rows_per_page": "Rows per page",
+        "of": "of",
+        "to": "to"
+      },
+      "messages": {
+        "copied_to_clipboard": "Copied to clipboard",
+        "confirm_action": "Are you sure?",
+        "are_you_sure_delete": "Are you sure you want to delete this item?",
+        "welcome": "Welcome",
+        "enjoy_the_event": "Enjoy the event!"
+      },
+      "misc": {
+        "all": "All",
+        "or": "or",
+        "example": "Example",
+        "options": "Options",
+        "total": "Total",
+        "type": "Type"
+      }
+    },
+    "metadata": {
+      "created_at": "Created at",
+      "updated_at": "Updated at",
+      "created_by": "Created by"
+    }
+  },
+  "auth": {
+    "login": "Login",
+    "logout": "Logout",
+    "registration": "Registration",
+    "confirm_password": "Confirm Password",
+    "agreed_terms": "I agree with the terms",
+    "agreed_privacy": "I agree with the privacy policy"
+  },
+  "business": {
+    "advertise": "Advertise",
+    "my_advertisements": "My Advertisements",
+    "negotiations": "Negotiations",
+    "opportunities": "Opportunities",
+    "plans": "Plans"
+  },
+  "validation": {
+    "rules": {
+      "required": "This field is required",
+      "email": "This field must be a valid email | These fields must be valid emails",
+      "date": "This field must be a valid date",
+      "min": "This field must have at least",
+      "max": "This field must have at most",
+      "characters": "characters",
+      "password": "Password must have at least 6 characters, one uppercase letter, one lowercase letter and one number",
+      "same_password": "Passwords must match",
+      "not_same_document": "The document must be unique for each participant",
+      "cpf": "This field must be a valid CPF",
+      "cnpj": "This field must be a valid CNPJ",
+      "cep": "This field must be a valid ZIP code",
+      "value_smaller_than_zero": "Value cannot be less than zero"
+    },
+    "permissions": {
+      "view": "You don't have permission to view this",
+      "create": "You don't have permission to create this",
+      "edit": "You don't have permission to edit this",
+      "delete": "You don't have permission to delete this",
+      "add": "You don't have permission to add this"
+    }
+  },
+  "http": {
+    "errors": {
+      "404": "Page not found",
+      "failed": "The action failed",
+      "no_records_found": "No records found"
+    },
+    "success": "The action was successful"
+  },
+  "events": {
+    "singular": "Event",
+    "plural": "Events",
+    "core": {
+      "basic_information": "Basic Information",
+      "schedule": "Schedule",
+      "opening": "Opening",
+      "total_capacity": "Total Capacity",
+      "unique_code": "Unique Code",
+      "unique_code_hint": "This code is automatically generated",
+      "list_of_allowed_documents": "List of allowed documents"
+    },
+    "tickets": {
+      "singular": "Ticket",
+      "plural": "Tickets",
+      "types_singular": "Ticket Type",
+      "types_plural": "Ticket Types",
+      "event_ticket": "Event Ticket",
+      "event_tickets": "Event Tickets",
+      "event_ticket_types": "Event Ticket Types",
+      "sales_start_date": "Sales Start Date",
+      "sales_end_date": "Sales End Date",
+      "max_per_user": "Maximum Tickets per User",
+      "max_per_user_hint": "0 for unlimited",
+      "quantity_available": "Available Quantity",
+      "quantity_sold": "Quantity Sold"
+    },
+    "location": {
+      "singular": "Location"
+    },
+    "attendance": {
+      "participant_singular": "Participant",
+      "participant_plural": "Participants",
+      "checked_in_at": "Checked in at",
+      "is_checked_in": "Is checked in"
+    }
+  },
+  "user": {
+    "singular": "User",
+    "plural": "Users",
+    "profile": {
+      "singular": "Profile",
+      "name_and_surname": "Name and Surname",
+      "birth_date": "Birth Date",
+      "personal_information": "Personal Information"
+    },
+    "preferences": {
+      "singular": "Preferences"
+    }
+  },
+  "orders": {
+    "singular": "Order",
+    "plural": "Orders",
+    "core": {
+      "new_order": "New Order",
+      "payment_received": "Payment Received",
+      "resume": "Order Resume",
+      "buyer_information": "Buyer Information",
+      "participant_information": "Participant Information",
+      "same_as_buyer": "Same as buyer",
+      "select_at_least_one_ticket": "Select at least one ticket",
+      "select_payment_method": "Select a payment method",
+      "exclusive_list": "Exclusive list",
+      "successful_payment": "Successful payment"
+    },
+    "statuses": {
+      "paid": "Paid",
+      "pending": "Pending",
+      "approved": "Approved",
+      "canceled": "Canceled",
+      "completed": "Completed",
+      "confirmed": "Confirmed",
+      "confirmation": "Confirmation"
+    },
+    "payment_methods": {
+      "credit_card": "Credit Card",
+      "boleto": "Boleto",
+      "pix": "Pix"
+    }
+  },
+  "ui": {
+    "navigation": {
+      "collapse_menu": "Collapse menu",
+      "expand_menu": "Expand menu",
+      "dashboard": "Dashboard",
+      "explore": "Explore",
+      "advertise": "Advertise",
+      "my_advertisements": "My Advertisements",
+      "negotiations": "Negotiations",
+      "opportunities": "Opportunities",
+      "plans": "Plans",
+      "events": "Events",
+      "event_tickets": "Event Tickets",
+      "event_ticket_types": "Event Ticket Types",
+      "orders": "Orders",
+      "sales": "Sales",
+      "participants": "Participants",
+      "users": "Users",
+      "profile": "Profile",
+      "interests": "Interests",
+      "registration": "Registration",
+      "wallet": "Wallet",
+      "settings": "Settings",
+      "city": "City",
+      "state": "State",
+      "country": "Country",
+      "exit": "Exit"
+    }
+  },
+  "charts": {
+    "nps": {
+      "promotion_zone": "Promotion Zone",
+      "promotion_zone_range": "Promotion Zone Range",
+      "quality_zone": "Quality Zone",
+      "quality_zone_range": "Quality Zone Range",
+      "refinement_zone": "Refinement Zone",
+      "refinement_zone_range": "Refinement Zone Range",
+      "critical_zone": "Critical Zone",
+      "critical_zone_range": "Critical Zone Range",
+      "promoters": "Promoters",
+      "passives": "Passives",
+      "detractors": "Detractors"
+    }
+  },
+  "dashboard": {
+    "currency_format": "$ {value}",
+    "cards": {
+      "total_earnings": "Total Earnings",
+      "tickets_sold": "Tickets Sold",
+      "registrations": "Registrations"
+    },
+    "charts": {
+      "tickets_by_type": {
+        "title": "Total tickets by type",
+        "labels": {
+          "vip": "VIP",
+          "track": "Track",
+          "box": "Box",
+          "courtesy": "Courtesy"
+        }
+      },
+      "participants_by_document": {
+        "title": "Percentage of CNPJ and CPF in registrations"
+      },
+      "sales_over_time": {
+        "title": "Sales Over Period",
+        "y_label": "Value ({currency})"
+      },
+      "registration_source": {
+        "title": "Registration Source",
+        "source": "Source",
+        "sources": {
+          "instagram": "Instagram",
+          "facebook": "Facebook",
+          "google": "Google",
+          "referral": "Referral"
+        }
+      }
+    }
+  }
+}

+ 335 - 0
src/i18n/locales/es.json

@@ -0,0 +1,335 @@
+{
+  "common": {
+    "actions": {
+      "save": "Guardar",
+      "cancel": "Cancelar",
+      "edit": "Editar",
+      "add": "Añadir",
+      "search": "Buscar",
+      "delete": "Eliminar",
+      "view": "Ver",
+      "back": "Volver",
+      "next": "Siguiente",
+      "resend_email": "Reenviar correo electrónico",
+      "download_certificate": "Descargar certificado",
+      "download_boleto": "Descargar Boleto",
+      "copy_paste_code": "Copie y pegue el código a continuación para realizar el pago"
+    },
+    "terms": {
+      "name": "Nombre",
+      "email": "Correo electrónico",
+      "password": "Contraseña",
+      "description": "Descripción",
+      "date": "Fecha",
+      "start_date": "Fecha de inicio",
+      "end_date": "Fecha de fin",
+      "code": "Código",
+      "title": "Título",
+      "status": "Estado",
+      "price": "Precio",
+      "quantity": "Cantidad",
+      "city": "Ciudad",
+      "state": "Estado/Provincia",
+      "country": "País",
+      "address": "Dirección",
+      "address_number": "Número",
+      "complement": "Complemento",
+      "postal_code": "Código postal",
+      "phone": "Teléfono",
+      "document": "Documento",
+      "document_type": "Tipo de documento",
+      "cpf": "CPF",
+      "cnpj": "CNPJ",
+      "cep": "Código Postal",
+      "order_number": "Número de pedido",
+      "order_amount": "Monto del pedido",
+      "total_amount": "Monto total",
+      "payment": "Pago",
+      "payment_method": "Método de pago",
+      "payment_date": "Fecha de pago",
+      "payment_amount": "Monto del pago",
+      "language": "Idioma",
+      "currency": "Moneda",
+      "interests": "Intereses",
+      "avatar": "Avatar",
+      "banner": "Banner",
+      "logo": "Logo",
+      "media": "Media",
+      "month": "Mes",
+      "week": "Semana",
+      "day": "Día",
+      "hour": "Hora",
+      "minute": "Minuto",
+      "second": "Segundo",
+      "year": "Año",
+      "all": "Todos",
+      "certificate": "Certificado",
+      "version": "Versión"
+    },
+    "months": {
+      "january": "Enero",
+      "february": "Febrero",
+      "march": "Marzo",
+      "april": "Abril",
+      "may": "Mayo",
+      "june": "Junio",
+      "july": "Julio",
+      "august": "Agosto",
+      "september": "Septiembre",
+      "october": "Octubre",
+      "november": "Noviembre",
+      "december": "Diciembre"
+    },
+    "status": {
+      "active": "Activo",
+      "inactive": "Inactivo",
+      "canceled": "Cancelado",
+      "loading": "Por favor espere...",
+      "yes": "Sí",
+      "no": "No"
+    },
+    "ui": {
+      "file": {
+        "choose": "Elegir un archivo",
+        "click_select": "Haga clic para seleccionar un archivo",
+        "click_select_image": "Haga clic para seleccionar una imagen",
+        "drag": "Arrastrar",
+        "drag_and_drop": "Arrastre y suelte el archivo aquí",
+        "drag_here": "Arrastre el archivo aquí",
+        "selected": "Archivo seleccionado"
+      },
+      "table": {
+        "rows_per_page": "Filas por página",
+        "of": "de",
+        "to": "a"
+      },
+      "messages": {
+        "copied_to_clipboard": "Copiado al portapapeles",
+        "confirm_action": "¿Estás seguro?",
+        "are_you_sure_delete": "¿Estás seguro de que quieres eliminar este elemento?",
+        "welcome": "Bienvenido",
+        "enjoy_the_event": "¡Disfruta el evento!"
+      },
+      "misc": {
+        "all": "Todos",
+        "or": "o",
+        "example": "Ejemplo",
+        "options": "Opciones",
+        "total": "Total",
+        "type": "Tipo"
+      }
+    },
+    "metadata": {
+      "created_at": "Creado el",
+      "updated_at": "Actualizado el",
+      "created_by": "Creado por"
+    }
+  },
+  "auth": {
+    "login": "Iniciar sesión",
+    "logout": "Cerrar sesión",
+    "registration": "Registro",
+    "confirm_password": "Confirmar contraseña",
+    "agreed_terms": "Acepto los términos",
+    "agreed_privacy": "Acepto la política de privacidad"
+  },
+  "business": {
+    "advertise": "Anunciar",
+    "my_advertisements": "Mis anuncios",
+    "negotiations": "Negociaciones",
+    "opportunities": "Oportunidades",
+    "plans": "Planes"
+  },
+  "validation": {
+    "rules": {
+      "required": "Este campo es obligatorio",
+      "email": "Este campo debe ser un correo electrónico válido | Estos campos deben ser correos electrónicos válidos",
+      "date": "Este campo debe ser una fecha válida",
+      "min": "Este campo debe tener al menos",
+      "max": "Este campo debe tener como máximo",
+      "characters": "caracteres",
+      "password": "La contraseña debe tener al menos 6 caracteres, una letra mayúscula, una letra minúscula y un número",
+      "same_password": "Las contraseñas deben coincidir",
+      "not_same_document": "El documento debe ser único para cada participante",
+      "cpf": "Este campo debe ser un CPF válido",
+      "cnpj": "Este campo debe ser un CNPJ válido",
+      "cep": "Este campo debe ser un código postal válido",
+      "value_smaller_than_zero": "El valor no puede ser menor que cero"
+    },
+    "permissions": {
+      "view": "No tienes permiso para ver esto",
+      "create": "No tienes permiso para crear esto",
+      "edit": "No tienes permiso para editar esto",
+      "delete": "No tienes permiso para eliminar esto",
+      "add": "No tienes permiso para añadir esto"
+    }
+  },
+  "http": {
+    "errors": {
+      "404": "Página no encontrada",
+      "failed": "La acción falló",
+      "no_records_found": "No se encontraron registros"
+    },
+    "success": "La acción fue exitosa"
+  },
+  "events": {
+    "singular": "Evento",
+    "plural": "Eventos",
+    "core": {
+      "basic_information": "Información básica",
+      "schedule": "Programa",
+      "opening": "Apertura",
+      "total_capacity": "Capacidad total",
+      "unique_code": "Código único",
+      "unique_code_hint": "Este código se genera automáticamente",
+      "list_of_allowed_documents": "Lista de documentos permitidos"
+    },
+    "tickets": {
+      "singular": "Entrada",
+      "plural": "Entradas",
+      "types_singular": "Tipo de Entrada",
+      "types_plural": "Tipos de Entrada",
+      "event_ticket": "Entrada del Evento",
+      "event_tickets": "Entradas del Evento",
+      "event_ticket_types": "Tipos de Entrada del Evento",
+      "sales_start_date": "Fecha de inicio de ventas",
+      "sales_end_date": "Fecha de fin de ventas",
+      "max_per_user": "Máximo de entradas por usuario",
+      "max_per_user_hint": "0 para ilimitado",
+      "quantity_available": "Cantidad disponible",
+      "quantity_sold": "Cantidad vendida"
+    },
+    "location": {
+      "singular": "Ubicación"
+    },
+    "attendance": {
+      "participant_singular": "Participante",
+      "participant_plural": "Participantes",
+      "checked_in_at": "Registrado el",
+      "is_checked_in": "Está registrado"
+    }
+  },
+  "user": {
+    "singular": "Usuario",
+    "plural": "Usuarios",
+    "profile": {
+      "singular": "Perfil",
+      "name_and_surname": "Nombre y Apellido",
+      "birth_date": "Fecha de nacimiento",
+      "personal_information": "Información personal"
+    },
+    "preferences": {
+      "singular": "Preferencias"
+    }
+  },
+  "orders": {
+    "singular": "Pedido",
+    "plural": "Pedidos",
+    "core": {
+      "new_order": "Nuevo pedido",
+      "payment_received": "Pago recibido",
+      "resume": "Resumen del pedido",
+      "buyer_information": "Información del comprador",
+      "participant_information": "Información del participante",
+      "same_as_buyer": "Igual que el comprador",
+      "select_at_least_one_ticket": "Seleccione al menos una entrada",
+      "select_payment_method": "Seleccione un método de pago",
+      "exclusive_list": "Lista exclusiva",
+      "successful_payment": "Pago exitoso"
+    },
+    "statuses": {
+      "paid": "Pagado",
+      "pending": "Pendiente",
+      "approved": "Aprobado",
+      "canceled": "Cancelado",
+      "completed": "Completado",
+      "confirmed": "Confirmado",
+      "confirmation": "Confirmación"
+    },
+    "payment_methods": {
+      "credit_card": "Tarjeta de crédito",
+      "boleto": "Boleto",
+      "pix": "Pix"
+    }
+  },
+  "ui": {
+    "navigation": {
+      "collapse_menu": "Contraer menú",
+      "expand_menu": "Expandir menú",
+      "dashboard": "Panel",
+      "explore": "Explorar",
+      "advertise": "Anunciar",
+      "my_advertisements": "Mis anuncios",
+      "negotiations": "Negociaciones",
+      "opportunities": "Oportunidades",
+      "plans": "Planes",
+      "events": "Eventos",
+      "event_tickets": "Entradas del Evento",
+      "event_ticket_types": "Tipos de Entrada del Evento",
+      "orders": "Pedidos",
+      "sales": "Ventas",
+      "participants": "Participantes",
+      "users": "Usuarios",
+      "profile": "Perfil",
+      "interests": "Intereses",
+      "registration": "Registro",
+      "wallet": "Billetera",
+      "settings": "Configuración",
+      "city": "Ciudad",
+      "state": "Estado/Provincia",
+      "country": "País",
+      "exit": "Salir"
+    }
+  },
+  "charts": {
+    "nps": {
+      "promotion_zone": "Zona de Promoción",
+      "promotion_zone_range": "Rango de Zona de Promoción",
+      "quality_zone": "Zona de Calidad",
+      "quality_zone_range": "Rango de Zona de Calidad",
+      "refinement_zone": "Zona de Refinamiento",
+      "refinement_zone_range": "Rango de Zona de Refinamiento",
+      "critical_zone": "Zona Crítica",
+      "critical_zone_range": "Rango de Zona Crítica",
+      "promoters": "Promotores",
+      "passives": "Pasivos",
+      "detractors": "Detractores"
+    }
+  },
+  "dashboard": {
+    "currency_format": "R$ {value}",
+    "cards": {
+      "total_earnings": "Ingresos Totales",
+      "tickets_sold": "Entradas Vendidas",
+      "registrations": "Inscripciones"
+    },
+    "charts": {
+      "tickets_by_type": {
+        "title": "Total de entradas por tipo",
+        "labels": {
+          "vip": "VIP",
+          "track": "Pista",
+          "box": "Palco",
+          "courtesy": "Cortesía"
+        }
+      },
+      "participants_by_document": {
+        "title": "Porcentaje de CNPJ y CPF en las inscripciones"
+      },
+      "sales_over_time": {
+        "title": "Ventas por Período",
+        "y_label": "Valor ({currency})"
+      },
+      "registration_source": {
+        "title": "Origen de la Inscripción",
+        "source": "Origen",
+        "sources": {
+          "instagram": "Instagram",
+          "facebook": "Facebook",
+          "google": "Google",
+          "referral": "Indicación"
+        }
+      }
+    }
+  }
+}

+ 335 - 0
src/i18n/locales/pt.json

@@ -0,0 +1,335 @@
+{
+  "common": {
+    "actions": {
+      "save": "Salvar",
+      "cancel": "Cancelar",
+      "edit": "Editar",
+      "add": "Adicionar",
+      "search": "Buscar",
+      "delete": "Excluir",
+      "view": "Visualizar",
+      "back": "Voltar",
+      "next": "Próximo",
+      "resend_email": "Reenviar e-mail",
+      "download_certificate": "Baixar certificado",
+      "download_boleto": "Baixar Boleto",
+      "copy_paste_code": "Copie e cole o código abaixo para efetuar o pagamento"
+    },
+    "terms": {
+      "name": "Nome",
+      "email": "E-mail",
+      "password": "Senha",
+      "description": "Descrição",
+      "date": "Data",
+      "start_date": "Data de Início",
+      "end_date": "Data de Fim",
+      "code": "Código",
+      "title": "Título",
+      "status": "Status",
+      "price": "Preço",
+      "quantity": "Quantidade",
+      "city": "Cidade",
+      "state": "Estado",
+      "country": "País",
+      "address": "Endereço",
+      "address_number": "Número",
+      "complement": "Complemento",
+      "postal_code": "Código Postal",
+      "phone": "Telefone",
+      "document": "Documento",
+      "document_type": "Tipo de Documento",
+      "cpf": "CPF",
+      "cnpj": "CNPJ",
+      "cep": "CEP",
+      "order_number": "Número do Pedido",
+      "order_amount": "Valor do Pedido",
+      "total_amount": "Valor Total",
+      "payment": "Pagamento",
+      "payment_method": "Método de Pagamento",
+      "payment_date": "Data do Pagamento",
+      "payment_amount": "Valor do Pagamento",
+      "language": "Idioma",
+      "currency": "Moeda",
+      "interests": "Interesses",
+      "avatar": "Avatar",
+      "banner": "Banner",
+      "logo": "Logo",
+      "media": "Mídia",
+      "month": "Mês",
+      "week": "Semana",
+      "day": "Dia",
+      "hour": "Hora",
+      "minute": "Minuto",
+      "second": "Segundo",
+      "year": "Ano",
+      "all": "Todos",
+      "certificate": "Certificado",
+      "version": "Versão"
+    },
+    "months": {
+      "january": "Janeiro",
+      "february": "Fevereiro",
+      "march": "Março",
+      "april": "Abril",
+      "may": "Maio",
+      "june": "Junho",
+      "july": "Julho",
+      "august": "Agosto",
+      "september": "Setembro",
+      "october": "Outubro",
+      "november": "Novembro",
+      "december": "Dezembro"
+    },
+    "status": {
+      "active": "Ativo",
+      "inactive": "Inativo",
+      "canceled": "Cancelado",
+      "loading": "Por favor, aguarde...",
+      "yes": "Sim",
+      "no": "Não"
+    },
+    "ui": {
+      "file": {
+        "choose": "Escolha um arquivo",
+        "click_select": "Clique para selecionar um arquivo",
+        "click_select_image": "Clique para selecionar uma imagem",
+        "drag": "Arraste",
+        "drag_and_drop": "Arraste e solte o arquivo aqui",
+        "drag_here": "Arraste o arquivo aqui",
+        "selected": "Arquivo selecionado"
+      },
+      "table": {
+        "rows_per_page": "Linhas por página",
+        "of": "de",
+        "to": "a"
+      },
+      "messages": {
+        "copied_to_clipboard": "Copiado para a área de transferência",
+        "confirm_action": "Você tem certeza?",
+        "are_you_sure_delete": "Tem certeza de que deseja excluir este item?",
+        "welcome": "Bem-vindo",
+        "enjoy_the_event": "Aproveite o evento!"
+      },
+      "misc": {
+        "all": "Todos",
+        "or": "ou",
+        "example": "Exemplo",
+        "options": "Opções",
+        "total": "Total",
+        "type": "Tipo"
+      }
+    },
+    "metadata": {
+      "created_at": "Criado em",
+      "updated_at": "Atualizado em",
+      "created_by": "Criado por"
+    }
+  },
+  "auth": {
+    "login": "Login",
+    "logout": "Sair",
+    "registration": "Cadastro",
+    "confirm_password": "Confirmar Senha",
+    "agreed_terms": "Eu concordo com os termos",
+    "agreed_privacy": "Eu concordo com a política de privacidade"
+  },
+  "business": {
+    "advertise": "Anunciar",
+    "my_advertisements": "Meus Anúncios",
+    "negotiations": "Negociações",
+    "opportunities": "Oportunidades",
+    "plans": "Planos"
+  },
+  "validation": {
+    "rules": {
+      "required": "Este campo é obrigatório",
+      "email": "Este campo deve ser um e-mail válido | Estes campos devem ser e-mails válidos",
+      "date": "Este campo deve ser uma data válida",
+      "min": "Este campo deve ter no mínimo",
+      "max": "Este campo deve ter no máximo",
+      "characters": "caracteres",
+      "password": "A senha deve ter pelo menos 6 caracteres, uma letra maiúscula, uma letra minúscula e um número",
+      "same_password": "As senhas devem ser iguais",
+      "not_same_document": "O documento deve ser único para cada participante",
+      "cpf": "Este campo deve ser um CPF válido",
+      "cnpj": "Este campo deve ser um CNPJ válido",
+      "cep": "Este campo deve ser um CEP válido",
+      "value_smaller_than_zero": "O valor não pode ser menor que zero"
+    },
+    "permissions": {
+      "view": "Você não tem permissão para visualizar isto",
+      "create": "Você não tem permissão para criar isto",
+      "edit": "Você não tem permissão para editar isto",
+      "delete": "Você não tem permissão para excluir isto",
+      "add": "Você não tem permissão para adicionar isto"
+    }
+  },
+  "http": {
+    "errors": {
+      "404": "Página não encontrada",
+      "failed": "A ação falhou",
+      "no_records_found": "Nenhum registro encontrado"
+    },
+    "success": "A ação foi bem-sucedida"
+  },
+  "events": {
+    "singular": "Evento",
+    "plural": "Eventos",
+    "core": {
+      "basic_information": "Informações Básicas",
+      "schedule": "Programação",
+      "opening": "Abertura",
+      "total_capacity": "Capacidade Total",
+      "unique_code": "Código Único",
+      "unique_code_hint": "Este código é gerado automaticamente",
+      "list_of_allowed_documents": "Lista de documentos permitidos"
+    },
+    "tickets": {
+      "singular": "Ingresso",
+      "plural": "Ingressos",
+      "types_singular": "Tipo de Ingresso",
+      "types_plural": "Tipos de Ingresso",
+      "event_ticket": "Ingresso do Evento",
+      "event_tickets": "Ingressos do Evento",
+      "event_ticket_types": "Tipos de Ingresso do Evento",
+      "sales_start_date": "Data de Início das Vendas",
+      "sales_end_date": "Data de Fim das Vendas",
+      "max_per_user": "Máximo de Ingressos por Usuário",
+      "max_per_user_hint": "0 para ilimitado",
+      "quantity_available": "Quantidade Disponível",
+      "quantity_sold": "Quantidade Vendida"
+    },
+    "location": {
+      "singular": "Localização"
+    },
+    "attendance": {
+      "participant_singular": "Participante",
+      "participant_plural": "Participantes",
+      "checked_in_at": "Check-in em",
+      "is_checked_in": "Check-in realizado"
+    }
+  },
+  "user": {
+    "singular": "Usuário",
+    "plural": "Usuários",
+    "profile": {
+      "singular": "Perfil",
+      "name_and_surname": "Nome e Sobrenome",
+      "birth_date": "Data de Nascimento",
+      "personal_information": "Informações Pessoais"
+    },
+    "preferences": {
+      "singular": "Preferências"
+    }
+  },
+  "orders": {
+    "singular": "Pedido",
+    "plural": "Pedidos",
+    "core": {
+      "new_order": "Novo Pedido",
+      "payment_received": "Pagamento Recebido",
+      "resume": "Resumo do Pedido",
+      "buyer_information": "Informações do Comprador",
+      "participant_information": "Informações do Participante",
+      "same_as_buyer": "O mesmo que o comprador",
+      "select_at_least_one_ticket": "Selecione pelo menos um ingresso",
+      "select_payment_method": "Selecione um método de pagamento",
+      "exclusive_list": "Lista exclusiva",
+      "successful_payment": "Pagamento bem-sucedido"
+    },
+    "statuses": {
+      "paid": "Pago",
+      "pending": "Pendente",
+      "approved": "Aprovado",
+      "canceled": "Cancelado",
+      "completed": "Concluído",
+      "confirmed": "Confirmado",
+      "confirmation": "Confirmação"
+    },
+    "payment_methods": {
+      "credit_card": "Cartão de Crédito",
+      "boleto": "Boleto",
+      "pix": "Pix"
+    }
+  },
+  "ui": {
+    "navigation": {
+      "collapse_menu": "Recolher menu",
+      "expand_menu": "Expandir menu",
+      "dashboard": "Painel",
+      "explore": "Explorar",
+      "advertise": "Anunciar",
+      "my_advertisements": "Meus Anúncios",
+      "negotiations": "Negociações",
+      "opportunities": "Oportunidades",
+      "plans": "Planos",
+      "events": "Eventos",
+      "event_tickets": "Ingressos do Evento",
+      "event_ticket_types": "Tipos de Ingresso",
+      "orders": "Pedidos",
+      "sales": "Vendas",
+      "participants": "Participantes",
+      "users": "Usuários",
+      "profile": "Perfil",
+      "interests": "Interesses",
+      "registration": "Cadastro",
+      "wallet": "Carteira",
+      "settings": "Configurações",
+      "city": "Cidade",
+      "state": "Estado",
+      "country": "País",
+      "exit": "Sair"
+    }
+  },
+  "charts": {
+    "nps": {
+      "promotion_zone": "Zona de Promoção",
+      "promotion_zone_range": "Faixa da Zona de Promoção",
+      "quality_zone": "Zona de Qualidade",
+      "quality_zone_range": "Faixa da Zona de Qualidade",
+      "refinement_zone": "Zona de Aperfeiçoamento",
+      "refinement_zone_range": "Faixa da Zona de Aperfeiçoamento",
+      "critical_zone": "Zona Crítica",
+      "critical_zone_range": "Faixa da Zona Crítica",
+      "promoters": "Promotores",
+      "passives": "Passivos",
+      "detractors": "Detratores"
+    }
+  },
+  "dashboard": {
+    "currency_format": "R$ {value}",
+    "cards": {
+      "total_earnings": "Total Ganho",
+      "tickets_sold": "Ingressos Vendidos",
+      "registrations": "Cadastros"
+    },
+    "charts": {
+      "tickets_by_type": {
+        "title": "Total de ingressos por tipo",
+        "labels": {
+          "vip": "VIP",
+          "track": "Pista",
+          "box": "Camarote",
+          "courtesy": "Cortesia"
+        }
+      },
+      "participants_by_document": {
+        "title": "Porcentagem de CNPJ e CPF por cadastros"
+      },
+      "sales_over_time": {
+        "title": "Vendas por Período",
+        "y_label": "Valor ({currency})"
+      },
+      "registration_source": {
+        "title": "Origem dos Cadastros",
+        "source": "Origem",
+        "sources": {
+          "instagram": "Instagram",
+          "facebook": "Facebook",
+          "google": "Google",
+          "referral": "Indicação"
+        }
+      }
+    }
+  }
+}

+ 7 - 0
src/layouts/LoginLayout.vue

@@ -0,0 +1,7 @@
+<template>
+  <q-layout view="hHh lpR fFf">
+    <q-page-container>
+      <router-view />
+    </q-page-container>
+  </q-layout>
+</template>

+ 145 - 0
src/layouts/MainLayout.vue

@@ -0,0 +1,145 @@
+<template>
+  <q-layout class="relative" view="hHh lpR fFf">
+    <LeftMenuLayout v-if="!$q.screen.lt.sm" />
+    <LeftMenuLayoutMobile v-else v-model="leftDrawerOpen" />
+    <q-header v-if="$q.screen.lt.sm" class="bg-transparent q-pa-sm">
+      <q-toolbar
+        class="flex justify-between bg-surface"
+        style="border-radius: 6px !important"
+      >
+        <q-btn dense flat @click="toggleLeftDrawer">
+          <q-icon name="menu" :color="$q.dark.isActive ? 'white' : 'black'" />
+        </q-btn>
+        <q-btn dense flat>
+          <img
+            :src="someAvatar()"
+            alt="avatar"
+            style="width: 20px; height: 20px; border-radius: 50%"
+          />
+          <q-menu anchor="center right" self="top start">
+            <q-list class="column no-wrap overflow-hidden">
+              <q-item
+                v-ripple
+                v-close-popup
+                clickable
+                :to="{ name: 'ProfilePage' }"
+                exact
+                exact-active-class="menu-selected"
+              >
+                <div class="flex">
+                  <q-item-section avatar>
+                    <q-icon
+                      name="account_circle"
+                      color="primary"
+                      style="font-size: 18px"
+                    />
+                  </q-item-section>
+                  <q-item-section>{{
+                    $t("user.profile.singular")
+                  }}</q-item-section>
+                </div>
+              </q-item>
+              <q-item v-ripple clickable @click="logoutFn">
+                <div class="flex">
+                  <q-item-section avatar>
+                    <q-icon
+                      name="logout"
+                      color="negative"
+                      style="font-size: 18px"
+                    />
+                  </q-item-section>
+                  <q-item-section>{{ $t("auth.logout") }}</q-item-section>
+                </div>
+              </q-item>
+            </q-list>
+          </q-menu>
+        </q-btn>
+      </q-toolbar>
+    </q-header>
+    <q-page-container>
+      <q-page>
+        <q-scroll-area
+          ref="scrollAreaRef"
+          :style="
+            $q.screen.lt.sm
+              ? 'height: calc(100dvh - 68px - env(safe-area-inset-top)) !important;'
+              : 'height: calc(100dvh - env(safe-area-inset-top)) !important;'
+          "
+        >
+          <router-view v-slot="{ Component }">
+            <Transition mode="out-in">
+              <component
+                :is="Component"
+                style="padding: 20px !important; padding-right: 10px !important"
+                :style="$q.screen.lt.sm ? 'padding-left: 10px !important;' : ''"
+              />
+            </Transition>
+          </router-view>
+        </q-scroll-area>
+      </q-page>
+    </q-page-container>
+  </q-layout>
+</template>
+
+<script setup>
+import { ref, useTemplateRef, watch } from "vue";
+import { useRoute } from "vue-router";
+import { useAuth } from "src/composables/useAuth";
+import { useRouter } from "vue-router";
+import LeftMenuLayout from "src/components/layout/LeftMenuLayout.vue";
+import LeftMenuLayoutMobile from "src/components/layout/LeftMenuLayoutMobile.vue";
+
+defineOptions({
+  name: "MainLayout",
+});
+
+const { logout } = useAuth();
+const route = useRoute();
+const leftDrawerOpen = ref(false);
+const scrollAreaRef = useTemplateRef("scrollAreaRef");
+const router = useRouter();
+
+let oldValue = route.path;
+
+const someAvatar = () => {
+  return "https://cdn.quasar.dev/img/avatar4.jpg";
+};
+
+const logoutFn = async () => {
+  await logout();
+  router.push({ name: "LoginPage" });
+};
+
+const toggleLeftDrawer = () => {
+  leftDrawerOpen.value = !leftDrawerOpen.value;
+};
+
+watch(route, (value) => {
+  if (oldValue.path != value.path) {
+    scrollAreaRef.value.setScrollPosition("vertical", 0, 0);
+    scrollAreaRef.value.setScrollPosition("horizontal", 0, 0);
+  }
+  oldValue = value.path;
+});
+</script>
+<style scoped>
+.v-enter-active {
+  opacity: 1;
+  transition: all 0.15s ease-in;
+}
+
+.v-leave-active {
+  opacity: 1;
+  transition: all 0.15s ease-out;
+}
+
+.v-enter-from,
+.v-leave-to {
+  opacity: 0;
+  transition: all 0.15s ease-in;
+}
+
+.v-leave-to {
+  transition: all 0.15s ease-out;
+}
+</style>

+ 30 - 0
src/pages/ErrorNotFound.vue

@@ -0,0 +1,30 @@
+<template>
+  <div
+    class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center"
+  >
+    <div>
+      <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
+      <div style="font-size: 30vh">{{ "404" }}</div>
+
+      <div class="text-h2" style="opacity: 0.4">
+        {{ $t("http.errors.404") }}
+      </div>
+
+      <q-btn
+        class="q-mt-xl"
+        color="white"
+        text-color="blue"
+        unelevated
+        to="/"
+        label="Go Home"
+        no-caps
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({
+  name: "ErrorNotFound",
+});
+</script>

+ 136 - 0
src/pages/LoginPage.vue

@@ -0,0 +1,136 @@
+<template>
+  <q-page padding class="login-page">
+    <q-card flat class="login-card q-pa-md q-pt-xl bg-surface">
+      <div class="text-center">
+        <q-img :src="Logo" style="max-width: 250px" />
+        <div class="text-h6">{{ $t("common.ui.messages.welcome") }}</div>
+      </div>
+
+      <q-form
+        ref="loginForm"
+        class="q-pa-md"
+        autocorrect="off"
+        autocapitalize="off"
+        autocomplete="off"
+        spellcheck="false"
+        @submit="submitLogin"
+      >
+        <q-card-section class="q-mt-sm">
+          <q-input
+            v-model="email"
+            filled
+            type="email"
+            label="Email"
+            lazy-rules
+            autofocus
+            :rules="[inputRules.required, inputRules.email]"
+          />
+
+          <DefaultPasswordInput
+            v-model="password"
+            :rules="[inputRules.required, inputRules.min(6)]"
+          />
+
+          <q-checkbox v-model="checkbox" label="Lembrar email" />
+        </q-card-section>
+
+        <q-card-actions align="right">
+          <q-btn
+            color="primary"
+            :label="$t('auth.login')"
+            size="md"
+            padding="md"
+            type="submit"
+            :loading="submitting"
+          >
+            <template #loading>
+              <q-spinner />
+            </template>
+          </q-btn>
+        </q-card-actions>
+      </q-form>
+    </q-card>
+  </q-page>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import { useQuasar } from "quasar";
+import { useAuth } from "src/composables/useAuth";
+import { useRouter } from "vue-router";
+import { useInputRules } from "src/composables/useInputRules";
+
+import Logo from "src/assets/logo.png";
+import DefaultPasswordInput from "src/components/defaults/DefaultPasswordInput.vue";
+
+const router = useRouter();
+const $q = useQuasar();
+
+const { inputRules } = useInputRules();
+const email = ref("");
+const password = ref(process.env.PASSWORD);
+const submitting = ref(false);
+const loginForm = ref(null);
+const checkbox = ref(false);
+
+const submitLogin = async () => {
+  try {
+    submitting.value = true;
+
+    const validate = await loginForm.value.validate();
+
+    if (!validate) {
+      return;
+    }
+
+    const email_storage = $q.cookies.get("email");
+
+    if (email_storage && !checkbox.value) {
+      $q.cookies.remove("email");
+    }
+    if (checkbox.value) {
+      $q.cookies.set("email", email.value, {
+        expires: "3d",
+        path: "/",
+        sameSite: "Lax",
+      });
+    }
+
+    await useAuth().login(email.value, password.value);
+
+    submitting.value = false;
+
+    router.push({ name: "DashboardPage" });
+  } catch (error) {
+    console.error(error);
+    submitting.value = false;
+  }
+};
+
+onMounted(() => {
+  const email_storage = $q.cookies.get("email");
+
+  if (email_storage) {
+    checkbox.value = true;
+    email.value = email_storage;
+  }
+
+  if (process.env.DEV && process.env.SENHA) {
+    password.value = process.env.SENHA;
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.login-page {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+
+  .login-card {
+    width: 100%;
+    max-width: 500px;
+    border-radius: 12px;
+  }
+}
+</style>

+ 130 - 0
src/pages/city/CityPage.vue

@@ -0,0 +1,130 @@
+<template>
+  <div>
+    <DefaultHeaderPage />
+    <div>
+      <DefaultTable
+        ref="tableRef"
+        :columns="columns"
+        :api-call="getCities"
+        :delete-function="deleteCity"
+        :mostrar-selecao-de-colunas="false"
+        :mostrar-botao-fullscreen="false"
+        :mostrar-toggle-inativos="false"
+        open-item
+        add-item
+        @on-row-click="onRowClick"
+        @on-add-item="onAddItem"
+      />
+    </div>
+  </div>
+</template>
+<script setup>
+import { defineAsyncComponent, useTemplateRef } from "vue";
+import { useQuasar } from "quasar";
+import { useI18n } from "vue-i18n";
+import { permissionStore } from "src/stores/permission";
+import { getCities, deleteCity } from "src/api/city";
+
+import DefaultTable from "src/components/defaults/DefaultTable.vue";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+
+const AddEditCityDialog = defineAsyncComponent(
+  () => import("src/pages/city/components/AddEditCityDialog.vue"),
+);
+
+const permission_store = permissionStore();
+const $q = useQuasar();
+const tableRef = useTemplateRef("tableRef");
+const { t } = useI18n();
+
+const columns = [
+  {
+    name: "id",
+    label: "ID",
+    field: "id",
+    align: "left",
+    required: true,
+    sortable: true,
+  },
+  {
+    name: "nome",
+    label: t("common.terms.name"),
+    field: "name",
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "state",
+    label: t("ui.navigation.state"),
+    field: (row) => row.state.name,
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "country",
+    label: t("ui.navigation.country"),
+    field: (row) => row.country.name,
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "status",
+    label: t("common.terms.status"),
+    field: (row) =>
+      row.status == "ACTIVE" ? t("common.status.active") : t("common.status.inactive"),
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "actions",
+    required: true,
+  },
+];
+
+const onRowClick = ({ row }) => {
+  if (permission_store.getAccess("config.city", "edit") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.edit"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditCityDialog,
+    componentProps: {
+      city: row,
+      title: () =>
+        useI18n().t("common.actions.edit") +
+        " " +
+        useI18n().t("ui.navigation.city"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+
+const onAddItem = () => {
+  if (permission_store.getAccess("config.city", "add") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.add"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditCityDialog,
+    componentProps: {
+      title: () =>
+        useI18n().t("common.actions.add") + " " + useI18n().t("ui.navigation.city"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+</script>

+ 158 - 0
src/pages/city/components/AddEditCityDialog.vue

@@ -0,0 +1,158 @@
+<template>
+  <q-dialog ref="dialogRef" @hide="onDialogHide">
+    <q-card class="q-dialog-plugin overflow-hidden" style="width: 800px">
+      <DefaultDialogHeader :title="title" @close="onDialogCancel" />
+      <q-form ref="formRef" @submit="onOKClick">
+        <q-card-section class="row q-col-gutter-sm">
+          <q-input
+            v-model="form.name"
+            :label="$t('common.terms.name')"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.name"
+            :error-message="serverErrors?.name"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.name = null"
+          />
+          <CountrySelect
+            ref="countrySelectRef"
+            v-model="selectedCountry"
+            :label="$t('ui.navigation.country')"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.country_id"
+            :error-message="serverErrors?.country_id"
+            :initial-id="city ? city.country_id : null"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.country_id = null"
+          />
+          <StateSelect
+            v-model="selectedState"
+            :country="selectedCountry"
+            :initial-id="form.state_id"
+            :label="$t('ui.navigation.state')"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.state_id"
+            :error-message="serverErrors?.state_id"
+            class="col-md-6 col-12"
+            @selected-country-id="countrySelectRef.selectCountryById($event)"
+            @update:model-value="serverErrors.state_id = null"
+          />
+          <q-select
+            v-model="selectedStatus"
+            :label="$t('common.terms.status')"
+            :options="statusOptions"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.status"
+            :error-message="serverErrors?.status"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.status = null"
+          />
+        </q-card-section>
+        <q-card-actions align="center">
+          <q-btn color="primary" label="Cancel" @click="onDialogCancel" />
+          <q-space />
+          <q-btn
+            color="primary"
+            label="OK"
+            :type="'submit'"
+            :loading="loading"
+            :disable="!hasUpdatedFields"
+          />
+        </q-card-actions>
+      </q-form>
+    </q-card>
+  </q-dialog>
+</template>
+<script setup>
+import { ref, useTemplateRef, onMounted, watch } from "vue";
+import { useInputRules } from "src/composables/useInputRules";
+import { useDialogPluginComponent } from "quasar";
+import { useI18n } from "vue-i18n";
+import { createCity, updateCity } from "src/api/city";
+import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
+import { useSubmitHandler } from "src/composables/useSubmitHandler";
+
+import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
+import CountrySelect from "src/components/regions/CountrySelect.vue";
+import StateSelect from "src/components/regions/StateSelect.vue";
+
+defineEmits([
+  // REQUIRED; need to specify some events that your
+  // component will emit through useDialogPluginComponent()
+  ...useDialogPluginComponent.emits,
+]);
+
+const { city, title } = defineProps({
+  city: {
+    type: Object,
+    default: null,
+  },
+  title: {
+    type: Function,
+    default: () => useI18n().t("common.terms.title"),
+  },
+});
+
+const { t } = useI18n();
+const { inputRules } = useInputRules();
+
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
+  useDialogPluginComponent();
+
+const formRef = useTemplateRef("formRef");
+const countrySelectRef = useTemplateRef("countrySelectRef");
+
+const { form, getUpdatedFields, hasUpdatedFields } = useFormUpdateTracker({
+  name: city ? city?.name : "",
+  country_id: city ? city?.country_id : null,
+  state_id: city ? city?.state_id : null,
+  status: city ? city?.status : "ACTIVE",
+});
+
+const {
+  loading,
+  serverErrors,
+  execute: submitForm,
+} = useSubmitHandler({
+  onSuccess: () => onDialogOK(true),
+  formRef: formRef,
+});
+
+const selectedCountry = ref(null);
+const selectedState = ref(null);
+const selectedStatus = ref({
+  label: t("common.status.active"),
+  value: "ACTIVE",
+});
+const statusOptions = ref([
+  { label: t("common.status.active"), value: "ACTIVE" },
+  { label: t("common.status.inactive"), value: "INACTIVE" },
+]);
+
+const onOKClick = async () => {
+  if (city) {
+    await submitForm(() => updateCity(getUpdatedFields.value, city.id));
+  } else {
+    await submitForm(() => createCity({ ...form }));
+  }
+};
+
+watch(selectedStatus, () => {
+  form.status = selectedStatus.value?.value;
+});
+
+watch(selectedCountry, () => {
+  form.country_id = selectedCountry.value?.value;
+});
+
+watch(selectedState, () => {
+  form.state_id = selectedState.value?.value;
+});
+
+onMounted(async () => {
+  if (city) {
+    selectedStatus.value = statusOptions.value.find(
+      (status) => status.value === city.status,
+    );
+  }
+});
+</script>

+ 125 - 0
src/pages/country/CountryPage.vue

@@ -0,0 +1,125 @@
+<template>
+  <div>
+    <DefaultHeaderPage />
+    <div>
+      <DefaultTable
+        ref="tableRef"
+        :columns="columns"
+        :api-call="getCountries"
+        :delete-function="deleteCountry"
+        :mostrar-selecao-de-colunas="false"
+        :mostrar-botao-fullscreen="false"
+        :mostrar-toggle-inativos="false"
+        open-item
+        add-item
+        @on-row-click="onRowClick"
+        @on-add-item="onAddItem"
+      />
+    </div>
+  </div>
+</template>
+<script setup>
+import { defineAsyncComponent, useTemplateRef } from "vue";
+import { useQuasar } from "quasar";
+import { useI18n } from "vue-i18n";
+import { permissionStore } from "src/stores/permission";
+import { getCountries, deleteCountry } from "src/api/country";
+
+import DefaultTable from "src/components/defaults/DefaultTable.vue";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+
+const AddEditCountryDialog = defineAsyncComponent(
+  () => import("src/pages/country/components/AddEditCountryDialog.vue"),
+);
+
+const permission_store = permissionStore();
+const $q = useQuasar();
+const tableRef = useTemplateRef("tableRef");
+const { t } = useI18n();
+
+const columns = [
+  {
+    name: "id",
+    label: "ID",
+    field: "id",
+    align: "left",
+    required: true,
+    sortable: true,
+  },
+  {
+    name: "nome",
+    label: t("common.terms.name"),
+    field: "name",
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "code",
+    label: t("common.terms.code"),
+    field: "code",
+    align: "center",
+    sortable: true,
+  },
+  {
+    name: "status",
+    label: t("common.terms.status"),
+    field: (row) =>
+      row.status == "ACTIVE" ? t("common.status.active") : t("common.status.inactive"),
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "actions",
+    required: true,
+  },
+];
+
+const onRowClick = ({ row }) => {
+  if (permission_store.getAccess("config.country", "edit") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.edit"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditCountryDialog,
+    componentProps: {
+      country: row,
+      title: () =>
+        useI18n().t("common.actions.edit") +
+        " " +
+        useI18n().t("ui.navigation.country"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+
+const onAddItem = () => {
+  if (permission_store.getAccess("config.country", "add") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.add"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditCountryDialog,
+    componentProps: {
+      title: () =>
+        useI18n().t("common.actions.add") +
+        " " +
+        useI18n().t("ui.navigation.country"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+</script>

+ 130 - 0
src/pages/country/components/AddEditCountryDialog.vue

@@ -0,0 +1,130 @@
+<template>
+  <q-dialog ref="dialogRef" @hide="onDialogHide">
+    <q-card class="q-dialog-plugin overflow-hidden" style="width: 800px">
+      <DefaultDialogHeader :title="title" @close="onDialogCancel" />
+      <q-form ref="formRef" @submit="onOKClick">
+        <q-card-section class="row q-col-gutter-sm">
+          <q-input
+            v-model="form.name"
+            :label="$t('common.terms.name')"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.name"
+            :error-message="serverErrors?.name"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.name = null"
+          />
+          <q-input
+            v-model="form.code"
+            :label="$t('common.terms.code')"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.code"
+            :error-message="serverErrors?.code"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.code = null"
+          />
+          <q-select
+            v-model="selectedStatus"
+            :label="$t('common.terms.status')"
+            :options="statusOptions"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.status"
+            :error-message="serverErrors?.status"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.status = null"
+          />
+        </q-card-section>
+        <q-card-actions align="center">
+          <q-btn color="primary" label="Cancel" @click="onDialogCancel" />
+          <q-space />
+          <q-btn
+            color="primary"
+            label="OK"
+            :type="'submit'"
+            :loading="loading"
+            :disable="!hasUpdatedFields"
+          />
+        </q-card-actions>
+      </q-form>
+    </q-card>
+  </q-dialog>
+</template>
+<script setup>
+import { ref, useTemplateRef, onMounted, watch } from "vue";
+import { useInputRules } from "src/composables/useInputRules";
+import { useDialogPluginComponent } from "quasar";
+import { useI18n } from "vue-i18n";
+import { createCountry, updateCountry } from "src/api/country";
+import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
+import { useSubmitHandler } from "src/composables/useSubmitHandler";
+
+import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
+
+defineEmits([
+  // REQUIRED; need to specify some events that your
+  // component will emit through useDialogPluginComponent()
+  ...useDialogPluginComponent.emits,
+]);
+
+const { country, title } = defineProps({
+  country: {
+    type: Object,
+    default: null,
+  },
+  title: {
+    type: Function,
+    default: () => useI18n().t("common.terms.title"),
+  },
+});
+
+const { t } = useI18n();
+const { inputRules } = useInputRules();
+
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
+  useDialogPluginComponent();
+
+const formRef = useTemplateRef("formRef");
+
+const { form, getUpdatedFields, hasUpdatedFields } = useFormUpdateTracker({
+  name: country ? country?.name : "",
+  code: country ? country?.code : "",
+  status: country ? country?.status : "ACTIVE",
+});
+
+const {
+  loading,
+  serverErrors,
+  execute: submitForm,
+} = useSubmitHandler({
+  onSuccess: () => onDialogOK(true),
+  formRef: formRef,
+});
+
+const selectedStatus = ref({
+  label: t("common.status.active"),
+  value: "ACTIVE",
+});
+const statusOptions = ref([
+  { label: t("common.status.active"), value: "ACTIVE" },
+  { label: t("common.status.inactive"), value: "INACTIVE" },
+]);
+
+const onOKClick = async () => {
+  if (country) {
+    await submitForm(() => updateCountry(getUpdatedFields.value, country.id));
+  } else {
+    await submitForm(() => createCountry({ ...form }));
+  }
+};
+
+watch(selectedStatus, () => {
+  form.status = selectedStatus.value.value;
+});
+
+onMounted(() => {
+  if (country) {
+    selectedStatus.value = statusOptions.value.find(
+      (status) => status.value === country.status,
+    );
+  }
+});
+</script>

+ 381 - 0
src/pages/dashboard/DashboardPage.vue

@@ -0,0 +1,381 @@
+<template>
+  <div>
+    <DefaultHeaderPage>
+      <template #after>
+        <q-btn
+          outline
+          icon="mdi-calendar"
+          color="primary"
+          @click="showFilter"
+        />
+      </template>
+    </DefaultHeaderPage>
+    <q-expansion-item
+      v-model="filter"
+      dense
+      hide-expand-icon
+      class="remove-header-expansion-item"
+    >
+      <DatePeriodSelector
+        v-model:selected-period="defaultPeriod"
+        v-model:selected-event-id="defaultEventId"
+        class="q-pa-sm"
+      />
+    </q-expansion-item>
+
+    <div v-if="!isLoading" class="column gap q-pa-sm">
+      <div class="flex full-width gap">
+        <div class="flex flex-grow gap">
+          <CardIconMiniChart
+            class="flex-grow"
+            :title="t('dashboard.cards.total_earnings')"
+            :icon="'mdi-currency-usd'"
+            :number-porcent="paymentsChart.percentage_change"
+            :number-card="
+              t('dashboard.currency_format', {
+                value: paymentsChart.current_total,
+              })
+            "
+          >
+            <template #chart>
+              <MiniLineChart
+                :data="paymentsChart.trend_data"
+                fill-color="rgba(0, 0, 0, 0)"
+              />
+            </template>
+          </CardIconMiniChart>
+          <CardIconMiniChart
+            class="flex-grow"
+            :title="t('orders.plural')"
+            :icon="'mdi-package-variant'"
+            :number-porcent="ordersChart.percentage_change"
+            :number-card="ordersChart.current_total"
+          >
+            <template #chart>
+              <MiniBarChart
+                :data="ordersChart.trend_data"
+                fill-color="rgba(0, 0, 0, 0)"
+              />
+            </template>
+          </CardIconMiniChart>
+        </div>
+        <div class="flex flex-grow gap">
+          <CardIconMiniChart
+            class="flex-grow"
+            :title="t('dashboard.cards.tickets_sold')"
+            :icon="'mdi-ticket-outline'"
+            :number-porcent="ticketsSoldChart.percentage_change"
+            :number-card="ticketsSoldChart.current_total"
+          >
+            <template #chart>
+              <MiniLineChart
+                :data="ticketsSoldChart.trend_data"
+                fill-color="rgba(0, 0, 0, 0)"
+              />
+            </template>
+          </CardIconMiniChart>
+          <CardIconMiniChart
+            class="flex-grow"
+            :title="t('dashboard.cards.registrations')"
+            :icon="'mdi-account-group-outline'"
+            :number-porcent="participantsChart.percentage_change"
+            :number-card="participantsChart.current_total"
+          >
+            <template #chart>
+              <MiniBarChart
+                :data="participantsChart.trend_data"
+                fill-color="rgba(0, 0, 0, 0)"
+              />
+            </template>
+          </CardIconMiniChart>
+        </div>
+      </div>
+
+      <div class="flex full-width gap">
+        <div class="flex flex-grow">
+          <CardIconChart
+            :title="t('dashboard.charts.tickets_by_type.title')"
+            :icon="'mdi-ticket-account'"
+            class="flex-grow"
+          >
+            <template #chart>
+              <BarChart
+                :data="eventTicketsByTypeChart"
+                :data-set-label="t('events.tickets.plural')"
+                :label-x="t('events.tickets.types_singular')"
+                :label-y="t('common.terms.quantity')"
+                :show-legend="true"
+              />
+            </template>
+          </CardIconChart>
+        </div>
+        <div class="flex flex-grow">
+          <CardIconChart
+            :title="t('dashboard.charts.participants_by_document.title')"
+            :icon="'mdi-badge-account'"
+            class="flex-grow"
+          >
+            <template #chart>
+              <DoughnutChart
+                :data="eventParticipantsByCNPJAndCPF"
+                :data-set-label="t('events.attendance.participant_plural')"
+              />
+            </template>
+          </CardIconChart>
+        </div>
+      </div>
+
+      <div class="flex full-width gap">
+        <div class="flex flex-grow">
+          <CardIconChart
+            :title="t('dashboard.charts.sales_over_time.title')"
+            :icon="'mdi-chart-line'"
+            class="flex-grow"
+          >
+            <template #chart>
+              <LineChart
+                :data="salesOverTimeLineChart"
+                :data-set-label="t('ui.navigation.sales')"
+                :label-x="t('common.terms.month')"
+                :label-y="
+                  t('dashboard.charts.sales_over_time.y_label', {
+                    currency: 'R$',
+                  })
+                "
+              />
+            </template>
+          </CardIconChart>
+        </div>
+        <div class="flex flex-grow">
+          <CardIconChart
+            :title="t('dashboard.charts.registration_source.title')"
+            :icon="'mdi-chart-pie'"
+            class="flex-grow"
+          >
+            <template #chart>
+              <PieChart
+                :data="eventSourcePieChart"
+                :data-set-label="
+                  t('dashboard.charts.registration_source.source')
+                "
+              />
+            </template>
+          </CardIconChart>
+        </div>
+      </div>
+    </div>
+
+    <div v-else class="flex flex-center full-width q-pa-xl">
+      <q-spinner color="primary" size="50px" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { onMounted, ref, watch, defineAsyncComponent } from "vue";
+import { useI18n } from "vue-i18n";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+import DatePeriodSelector from "./components/DatePeriodSelector.vue";
+
+const MiniLineChart = defineAsyncComponent(
+  () => import("src/components/charts/mini/MiniLineChart.vue"),
+);
+const MiniBarChart = defineAsyncComponent(
+  () => import("src/components/charts/mini/MiniBarChart.vue"),
+);
+const CardIconMiniChart = defineAsyncComponent(
+  () => import("src/components/charts/CardIconMiniChart.vue"),
+);
+const CardIconChart = defineAsyncComponent(
+  () => import("src/components/charts/CardIconChart.vue"),
+);
+const BarChart = defineAsyncComponent(
+  () => import("src/components/charts/normal/BarChart.vue"),
+);
+const DoughnutChart = defineAsyncComponent(
+  () => import("src/components/charts/normal/DoughnutChart.vue"),
+);
+const LineChart = defineAsyncComponent(
+  () => import("src/components/charts/normal/LineChart.vue"),
+);
+const PieChart = defineAsyncComponent(
+  () => import("src/components/charts/normal/PieChart.vue"),
+);
+
+const { t } = useI18n();
+
+const isLoading = ref(true);
+const filter = ref(false);
+const defaultPeriod = ref("month");
+const defaultEventId = ref(1);
+
+const ordersChart = ref({});
+const participantsChart = ref({});
+const paymentsChart = ref({});
+const ticketsSoldChart = ref({});
+const eventTicketsByTypeChart = ref({});
+const eventParticipantsByCNPJAndCPF = ref({});
+const salesOverTimeLineChart = ref({});
+const eventSourcePieChart = ref({});
+
+const showFilter = () => {
+  filter.value = !filter.value;
+};
+
+const generateMockData = () => {
+  const createMiniChartData = (currentTotal, percentage) => ({
+    current_total: currentTotal,
+    percentage_change: percentage,
+    trend_data: Array.from({ length: 10 }, () =>
+      Math.floor(Math.random() * 100),
+    ),
+  });
+
+  const barChartDataRaw = [
+    {
+      label: t("dashboard.charts.tickets_by_type.labels.vip"),
+      value: Math.floor(Math.random() * 300),
+    },
+    {
+      label: t("dashboard.charts.tickets_by_type.labels.track"),
+      value: Math.floor(Math.random() * 800),
+    },
+    {
+      label: t("dashboard.charts.tickets_by_type.labels.box"),
+      value: Math.floor(Math.random() * 400),
+    },
+    {
+      label: t("dashboard.charts.tickets_by_type.labels.courtesy"),
+      value: Math.floor(Math.random() * 50),
+    },
+  ];
+
+  const doughnutDataRaw = [
+    {
+      label: t("common.terms.cpf"),
+      value: Math.floor(Math.random() * 900 + 100),
+    },
+    {
+      label: t("common.terms.cnpj"),
+      value: Math.floor(Math.random() * 100 + 10),
+    },
+  ];
+  const doughnutTotal = doughnutDataRaw.reduce(
+    (sum, item) => sum + item.value,
+    0,
+  );
+
+  const lineChartDataRaw = [
+    {
+      label: t("common.months.january"),
+      value: Math.floor(1200 + Math.random() * 500),
+    },
+    {
+      label: t("common.months.february"),
+      value: Math.floor(1900 + Math.random() * 500),
+    },
+    {
+      label: t("common.months.march"),
+      value: Math.floor(3000 + Math.random() * 500),
+    },
+    {
+      label: t("common.months.april"),
+      value: Math.floor(5000 + Math.random() * 500),
+    },
+    {
+      label: t("common.months.may"),
+      value: Math.floor(2300 + Math.random() * 500),
+    },
+    {
+      label: t("common.months.june"),
+      value: Math.floor(3200 + Math.random() * 500),
+    },
+  ];
+
+  const pieDataRaw = [
+    {
+      label: t("dashboard.charts.registration_source.sources.instagram"),
+      value: Math.floor(450 + Math.random() * 50),
+    },
+    {
+      label: t("dashboard.charts.registration_source.sources.facebook"),
+      value: Math.floor(250 + Math.random() * 50),
+    },
+    {
+      label: t("dashboard.charts.registration_source.sources.google"),
+      value: Math.floor(180 + Math.random() * 50),
+    },
+    {
+      label: t("dashboard.charts.registration_source.sources.referral"),
+      value: Math.floor(120 + Math.random() * 50),
+    },
+  ];
+  const pieTotal = pieDataRaw.reduce((sum, item) => sum + item.value, 0);
+
+  return {
+    payments: createMiniChartData(
+      (Math.random() * 20000 + 5000).toFixed(2),
+      (Math.random() * 20 - 5).toFixed(2),
+    ),
+    orders: createMiniChartData(
+      Math.floor(Math.random() * 500 + 50),
+      (Math.random() * 15 - 5).toFixed(2),
+    ),
+    tickets_sold: createMiniChartData(
+      Math.floor(Math.random() * 1500 + 200),
+      (Math.random() * 25 - 5).toFixed(2),
+    ),
+    participants: createMiniChartData(
+      Math.floor(Math.random() * 1000 + 100),
+      (Math.random() * 10 - 5).toFixed(2),
+    ),
+    barData: {
+      chart_data: barChartDataRaw,
+    },
+    doughnutData: {
+      chart_data: doughnutDataRaw,
+      current_total: doughnutTotal,
+    },
+    lineData: {
+      chart_data: lineChartDataRaw,
+    },
+    pieData: {
+      chart_data: pieDataRaw,
+      current_total: pieTotal,
+    },
+  };
+};
+
+const updateDashboardData = async () => {
+  isLoading.value = true;
+  setTimeout(() => {
+    const mockData = generateMockData();
+
+    ordersChart.value = mockData.orders;
+    participantsChart.value = mockData.participants;
+    paymentsChart.value = mockData.payments;
+    ticketsSoldChart.value = mockData.tickets_sold;
+
+    eventTicketsByTypeChart.value = mockData.barData;
+    eventParticipantsByCNPJAndCPF.value = mockData.doughnutData;
+    salesOverTimeLineChart.value = mockData.lineData;
+    eventSourcePieChart.value = mockData.pieData;
+
+    isLoading.value = false;
+  }, 500);
+};
+
+watch([defaultPeriod, defaultEventId], async () => {
+  await updateDashboardData();
+});
+
+onMounted(async () => {
+  await updateDashboardData();
+});
+</script>
+
+<style scoped>
+.gap {
+  gap: 16px;
+}
+</style>

+ 47 - 0
src/pages/dashboard/components/DatePeriodSelector.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="row q-col-gutter-md items-center">
+    <div class="col-12 col-sm-6 col-md-3">
+      <q-btn-group outline class="full-width">
+        <q-btn
+          v-for="option in periodOptions"
+          :key="option.value"
+          :outline="selectedPeriod != option.value"
+          :label="option.label"
+          color="primary"
+          class="full-width"
+          @click="selectedPeriod = option.value"
+        />
+      </q-btn-group>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch } from "vue";
+import { useI18n } from "vue-i18n";
+
+const { t } = useI18n();
+
+const selectedPeriod = defineModel("selectedPeriod", {
+  type: String,
+  default: "month",
+});
+
+const selectedEventId = defineModel("selectedEventId", {
+  type: Number,
+});
+
+const selectedEvent = ref(null);
+
+watch(selectedEvent, () => {
+  selectedEventId.value = selectedEvent.value?.value;
+});
+
+const periodOptions = [
+  { label: t("common.terms.day"), value: "day" },
+  { label: t("common.terms.week"), value: "week" },
+  { label: t("common.terms.month"), value: "month" },
+  { label: t("common.terms.year"), value: "year" },
+  { label: t("common.terms.all"), value: "all" },
+];
+</script>

+ 125 - 0
src/pages/state/StatePage.vue

@@ -0,0 +1,125 @@
+<template>
+  <div>
+    <DefaultHeaderPage />
+    <div>
+      <DefaultTable
+        ref="tableRef"
+        :columns="columns"
+        :api-call="getStates"
+        :delete-function="deleteState"
+        :mostrar-selecao-de-colunas="false"
+        :mostrar-botao-fullscreen="false"
+        :mostrar-toggle-inativos="false"
+        open-item
+        add-item
+        @on-row-click="onRowClick"
+        @on-add-item="onAddItem"
+      />
+    </div>
+  </div>
+</template>
+<script setup>
+import { defineAsyncComponent, useTemplateRef } from "vue";
+import { useQuasar } from "quasar";
+import { useI18n } from "vue-i18n";
+import { permissionStore } from "src/stores/permission";
+import { getStates, deleteState } from "src/api/state";
+
+import DefaultTable from "src/components/defaults/DefaultTable.vue";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+
+const AddEditStateDialog = defineAsyncComponent(
+  () => import("src/pages/state/components/AddEditStateDialog.vue"),
+);
+
+const permission_store = permissionStore();
+const $q = useQuasar();
+const tableRef = useTemplateRef("tableRef");
+const { t } = useI18n();
+
+const columns = [
+  {
+    name: "id",
+    label: "ID",
+    field: "id",
+    align: "left",
+    required: true,
+    sortable: true,
+  },
+  {
+    name: "nome",
+    label: t("common.terms.name"),
+    field: "name",
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "country",
+    label: t("ui.navigation.country"),
+    field: (row) => row.country.name,
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "status",
+    label: t("common.terms.status"),
+    field: (row) =>
+      row.status == "ACTIVE" ? t("common.status.active") : t("common.status.inactive"),
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "actions",
+    required: true,
+  },
+];
+
+const onRowClick = ({ row }) => {
+  if (permission_store.getAccess("config.state", "edit") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.edit"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditStateDialog,
+    componentProps: {
+      state: row,
+      title: () =>
+        useI18n().t("common.actions.edit") +
+        " " +
+        useI18n().t("ui.navigation.state"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+
+const onAddItem = () => {
+  if (permission_store.getAccess("config.state", "add") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.add"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditStateDialog,
+    componentProps: {
+      title: () =>
+        useI18n().t("common.actions.add") +
+        " " +
+        useI18n().t("ui.navigation.state"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+</script>

+ 147 - 0
src/pages/state/components/AddEditStateDialog.vue

@@ -0,0 +1,147 @@
+<template>
+  <q-dialog ref="dialogRef" @hide="onDialogHide">
+    <q-card class="q-dialog-plugin overflow-hidden" style="width: 800px">
+      <DefaultDialogHeader :title="title" @close="onDialogCancel" />
+      <q-form ref="formRef" @submit="onOKClick">
+        <q-card-section class="row q-col-gutter-sm">
+          <q-input
+            v-model="form.name"
+            :label="$t('common.terms.name')"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.name"
+            :error-message="serverErrors?.name"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.name = null"
+          />
+          <q-input
+            v-model="form.code"
+            :label="$t('common.terms.code')"
+            :rules="[inputRules.required, inputRules.max(2)]"
+            :error="!!serverErrors?.code"
+            :error-message="serverErrors?.code"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.code = null"
+          />
+          <CountrySelect
+            v-model="selectedCountry"
+            :label="$t('ui.navigation.country')"
+            :rules="[inputRules.required]"
+            :initial-id="form.country_id"
+            :error="!!serverErrors?.country_id"
+            :error-message="serverErrors?.country_id"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.country_id = null"
+          />
+          <q-select
+            v-model="selectedStatus"
+            :label="$t('common.terms.status')"
+            :options="statusOptions"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.status"
+            :error-message="serverErrors?.status"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.status = null"
+          />
+        </q-card-section>
+        <q-card-actions align="center">
+          <q-btn color="primary" label="Cancel" @click="onDialogCancel" />
+          <q-space />
+          <q-btn
+            color="primary"
+            label="OK"
+            :type="'submit'"
+            :loading="loading"
+            :disable="!hasUpdatedFields"
+          />
+        </q-card-actions>
+      </q-form>
+    </q-card>
+  </q-dialog>
+</template>
+<script setup>
+import { ref, useTemplateRef, onMounted, watch } from "vue";
+import { useInputRules } from "src/composables/useInputRules";
+import { useDialogPluginComponent } from "quasar";
+import { useI18n } from "vue-i18n";
+import { createState, updateState } from "src/api/state";
+import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
+import { useSubmitHandler } from "src/composables/useSubmitHandler";
+
+import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
+import CountrySelect from "src/components/regions/CountrySelect.vue";
+
+defineEmits([
+  // REQUIRED; need to specify some events that your
+  // component will emit through useDialogPluginComponent()
+  ...useDialogPluginComponent.emits,
+]);
+
+const { state, title } = defineProps({
+  state: {
+    type: Object,
+    default: null,
+  },
+  title: {
+    type: Function,
+    default: () => useI18n().t("common.terms.title"),
+  },
+});
+
+const { t } = useI18n();
+const { inputRules } = useInputRules();
+
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
+  useDialogPluginComponent();
+
+const formRef = useTemplateRef("formRef");
+
+const { form, getUpdatedFields, hasUpdatedFields } = useFormUpdateTracker({
+  name: state ? state?.name : "",
+  code: state ? state?.code : "",
+  country_id: state ? state?.country_id : null,
+  status: state ? state?.status : "ACTIVE",
+});
+
+const {
+  loading,
+  serverErrors,
+  execute: submitForm,
+} = useSubmitHandler({
+  onSuccess: () => onDialogOK(true),
+  formRef: formRef,
+});
+
+const selectedCountry = ref(null);
+const selectedStatus = ref({
+  label: t("common.status.active"),
+  value: "ACTIVE",
+});
+const statusOptions = ref([
+  { label: t("common.status.active"), value: "ACTIVE" },
+  { label: t("common.status.inactive"), value: "INACTIVE" },
+]);
+
+const onOKClick = async () => {
+  if (state) {
+    await submitForm(() => updateState(getUpdatedFields.value, state.id));
+  } else {
+    await submitForm(() => createState({ ...form }));
+  }
+};
+
+watch(selectedCountry, () => {
+  form.country_id = selectedCountry.value.value;
+});
+
+watch(selectedStatus, () => {
+  form.status = selectedStatus.value.value;
+});
+
+onMounted(async () => {
+  if (state) {
+    selectedStatus.value = statusOptions.value.find(
+      (status) => status.value === state.status,
+    );
+  }
+});
+</script>

+ 108 - 0
src/pages/users/UsersPage.vue

@@ -0,0 +1,108 @@
+<template>
+  <div>
+    <DefaultHeaderPage />
+    <div>
+      <DefaultTable
+        ref="tableRef"
+        :columns="columns"
+        :api-call="getUsers"
+        :delete-function="deleteUser"
+        :mostrar-selecao-de-colunas="false"
+        :mostrar-botao-fullscreen="false"
+        :mostrar-toggle-inativos="false"
+        open-item
+        add-item
+        @on-row-click="onRowClick"
+        @on-add-item="onAddItem"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { defineAsyncComponent, useTemplateRef } from "vue";
+import { useQuasar } from "quasar";
+import { useI18n } from "vue-i18n";
+import { permissionStore } from "src/stores/permission";
+import { getUsers, deleteUser } from "src/api/user";
+
+import DefaultTable from "src/components/defaults/DefaultTable.vue";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+
+const AddEditUserDialog = defineAsyncComponent(
+  () => import("src/pages/users/components/AddEditUserDialog.vue"),
+);
+
+const permission_store = permissionStore();
+const $q = useQuasar();
+const { t } = useI18n();
+const tableRef = useTemplateRef("tableRef");
+
+const columns = [
+  {
+    name: "nome",
+    label: t("common.terms.name"),
+    field: "name",
+    align: "left",
+    required: true,
+    sortable: true,
+  },
+  {
+    name: "email",
+    label: "Email",
+    field: "email",
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "actions",
+    required: true,
+  },
+];
+
+const onRowClick = ({ row }) => {
+  if (permission_store.getAccess("config.user", "view") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.view"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditUserDialog,
+    componentProps: {
+      user: row,
+      title: () =>
+        useI18n().t("common.actions.edit") + " " + useI18n().t("user.singular"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+
+const onAddItem = () => {
+  if (permission_store.getAccess("config.user", "add") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.add"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditUserDialog,
+
+    componentProps: {
+      title: () =>
+        useI18n().t("common.actions.add") + " " + useI18n().t("user.singular"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+</script>

+ 140 - 0
src/pages/users/components/AddEditUserDialog.vue

@@ -0,0 +1,140 @@
+<template>
+  <q-dialog ref="dialogRef" @hide="onDialogHide">
+    <q-card class="q-dialog-plugin overflow-hidden" style="width: 800px">
+      <DefaultDialogHeader :title="title" @close="onDialogCancel" />
+      <q-form ref="formRef" @submit="onOKClick">
+        <q-card-section class="row q-col-gutter-sm">
+          <q-input
+            v-model="form.name"
+            :label="$t('common.terms.name')"
+            :hint="$t('user.profile.name_and_surname')"
+            :rules="[inputRules.required]"
+            :error="!!serverErrors?.name"
+            :error-message="serverErrors?.name"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.name = null"
+          />
+          <UserTypeSelect
+            v-model="selectedUserType"
+            :rules="[inputRules.required]"
+            :type="form.type"
+            :error="!!serverErrors?.email"
+            :error-message="serverErrors?.email"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.email = null"
+          />
+          <q-input
+            v-model="form.email"
+            label="Email"
+            :rules="[inputRules.email]"
+            :error="!!serverErrors?.type"
+            :error-message="serverErrors?.type"
+            class="col-12"
+            @update:model-value="serverErrors.type = null"
+          />
+          <DefaultPasswordInput
+            v-model="form.password"
+            :rules="
+              user
+                ? [inputRules.password]
+                : [inputRules.required, inputRules.password]
+            "
+            :error="!!serverErrors?.password"
+            :error-message="serverErrors?.password"
+            class="col-md-6 col-12"
+            @update:model-value="serverErrors.password = null"
+          />
+          <DefaultPasswordInput
+            v-model="confirmPassword"
+            :label="$t('auth.confirm_password')"
+            :rules="
+              user
+                ? [inputRules.samePassword(form.password)]
+                : [inputRules.required, inputRules.samePassword(form.password)]
+            "
+            class="col-md-6 col-12"
+          />
+        </q-card-section>
+        <q-card-actions align="center">
+          <q-btn color="primary" label="Cancel" @click="onDialogCancel" />
+          <q-space />
+          <q-btn
+            color="primary"
+            label="OK"
+            :type="'submit'"
+            :loading="loading"
+            :disable="!hasUpdatedFields"
+          />
+        </q-card-actions>
+      </q-form>
+    </q-card>
+  </q-dialog>
+</template>
+<script setup>
+import { ref, useTemplateRef, watch } from "vue";
+import { useInputRules } from "src/composables/useInputRules";
+import { useDialogPluginComponent } from "quasar";
+import { useI18n } from "vue-i18n";
+import { createUser, updateUser } from "src/api/user";
+import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
+import { useSubmitHandler } from "src/composables/useSubmitHandler";
+
+import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
+import DefaultPasswordInput from "src/components/defaults/DefaultPasswordInput.vue";
+import UserTypeSelect from "./UserTypeSelect.vue";
+
+defineEmits([
+  // REQUIRED; need to specify some events that your
+  // component will emit through useDialogPluginComponent()
+  ...useDialogPluginComponent.emits,
+]);
+
+const { user, title } = defineProps({
+  user: {
+    type: Object,
+    default: null,
+  },
+  title: {
+    type: Function,
+    default: () => useI18n().t("common.terms.title"),
+  },
+});
+
+const { inputRules } = useInputRules();
+
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
+  useDialogPluginComponent();
+
+const formRef = useTemplateRef("formRef");
+
+const { form, getUpdatedFields, hasUpdatedFields } = useFormUpdateTracker({
+  name: user ? user?.name : "",
+  email: user ? user?.email : "",
+  type: user ? user?.type : "",
+  password: "",
+});
+
+const selectedUserType = ref(null);
+const confirmPassword = ref("");
+
+const {
+  loading,
+  serverErrors,
+  execute: submitForm,
+} = useSubmitHandler({
+  onSuccess: () => onDialogOK(true),
+  formRef: formRef,
+});
+
+const onOKClick = async () => {
+  if (user) {
+    await submitForm(() => updateUser(getUpdatedFields.value, user.id));
+  } else {
+    await submitForm(() => createUser({ ...form }));
+  }
+};
+
+watch(selectedUserType, () => {
+  form.type = selectedUserType.value.value;
+});
+</script>

+ 108 - 0
src/pages/users/components/UserTypeSelect.vue

@@ -0,0 +1,108 @@
+<template>
+  <q-select
+    v-bind="$attrs"
+    v-model="selectedUserType"
+    use-input
+    hide-selected
+    fill-input
+    :options="filteredOptions"
+    :clearable
+    :loading
+    :readonly
+    :label
+    :rules
+    :error
+    :error-message
+    @filter="filterFn"
+  />
+</template>
+<script setup>
+import { onMounted, ref } from "vue";
+import { userTypes } from "src/api/user";
+import { useI18n } from "vue-i18n";
+
+const { label, rules, type } = defineProps({
+  label: {
+    type: String,
+    default: () => useI18n().t("common.ui.misc.type"),
+  },
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  type: {
+    type: String,
+    default: null,
+  },
+  clearable: {
+    type: Boolean,
+    default: true,
+  },
+  readonly: {
+    type: Boolean,
+    default: false,
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+});
+
+const selectedUserType = defineModel({
+  type: Object,
+});
+const userTypeOptions = ref([]);
+const filteredOptions = ref([]);
+const isLoading = ref(false);
+const searchQuery = ref("");
+
+const selectUserByValue = (value) => {
+  selectedUserType.value = userTypeOptions.value.find(
+    (option) => option.value === value,
+  );
+};
+
+const filterFn = (val, update) => {
+  searchQuery.value = val;
+  update(() => {
+    if (val === "") {
+      filteredOptions.value = userTypeOptions.value;
+    } else {
+      const needle = val.toLowerCase();
+      filteredOptions.value = userTypeOptions.value.filter((v) =>
+        v.label.toLowerCase().includes(needle),
+      );
+    }
+  });
+};
+
+onMounted(async () => {
+  try {
+    isLoading.value = true;
+    const response = await userTypes();
+    userTypeOptions.value = Object.entries(response).map(([key, value]) => ({
+      label: value,
+      value: key,
+    }));
+    filteredOptions.value = userTypeOptions.value;
+
+    if (type) {
+      selectedUserType.value = userTypeOptions.value.find(
+        (option) => option.value === type,
+      );
+    }
+  } catch (error) {
+    console.error("Failed to load user types:", error);
+  } finally {
+    isLoading.value = false;
+  }
+});
+
+defineExpose({
+  selectUserByValue,
+});
+</script>

+ 69 - 0
src/router/index.js

@@ -0,0 +1,69 @@
+import { defineRouter } from "#q-app/wrappers";
+import {
+  createRouter,
+  createMemoryHistory,
+  createWebHistory,
+  createWebHashHistory,
+} from "vue-router";
+import routes from "./routes";
+import { Notify } from "quasar";
+import { permissionStore } from "src/stores/permission";
+import { useI18n } from "vue-i18n";
+import { userStore } from "src/stores/user";
+import { useAuth } from "src/composables/useAuth";
+/*
+ * If not building with SSR mode, you can
+ * directly export the Router instantiation;
+ *
+ * The function below can be async too; either use
+ * async/await or return a Promise which resolves
+ * with the Router instance.
+ */
+
+export default defineRouter(function (/* { store, ssrContext } */) {
+  const createHistory = process.env.SERVER
+    ? createMemoryHistory
+    : process.env.VUE_ROUTER_MODE === "history"
+      ? createWebHistory
+      : createWebHashHistory;
+
+  const Router = createRouter({
+    scrollBehavior: () => ({ left: 0, top: 0 }),
+    routes,
+
+    // Leave this as is and make changes in quasar.conf.js instead!
+    // quasar.conf.js -> build -> vueRouterMode
+    // quasar.conf.js -> build -> publicPath
+    history: createHistory(process.env.VUE_ROUTER_BASE),
+  });
+  let refreshed = false;
+  Router.beforeEach(async (to, from, next) => {
+    if (userStore().accessToken == null && !refreshed) {
+      try {
+        await useAuth().refresh();
+      } catch {
+        refreshed = true;
+        return next({ name: "LoginPage" });
+      }
+    }
+    if (userStore().accessToken) {
+      if (to.name == "LoginPage") {
+        return next({ name: "DashboardPage" });
+      }
+    }
+    if (to.meta.requiredPermission) {
+      const { getAccess } = permissionStore();
+      const permission = getAccess(to.meta.requiredPermission, "view");
+      if (!permission) {
+        Notify.create({
+          message: useI18n().t("validation.permissions.view"),
+          type: "negative",
+        });
+        return next(from);
+      }
+    }
+    return next();
+  });
+
+  return Router;
+});

+ 57 - 0
src/router/routes.js

@@ -0,0 +1,57 @@
+let sub_routes = [];
+
+const modules = import.meta.glob("./routes/*.route.js", { eager: true });
+// eslint-disable-next-line no-unused-vars
+Object.entries(modules).forEach(([path, definition]) => {
+  sub_routes = sub_routes.concat(definition.default);
+});
+
+const routes = [
+  {
+    path: "/",
+    component: () => import("layouts/MainLayout.vue"),
+    meta: { requireAuth: true },
+    children: [
+      {
+        path: "",
+        name: "DashboardPage",
+        component: () => import("src/pages/dashboard/DashboardPage.vue"),
+        meta: {
+          title: "ui.navigation.dashboard",
+          requireAuth: true,
+          requiredPermission: "dashboard",
+          breadcrumbs: [
+            {
+              name: "DashboardPage",
+              title: "ui.navigation.dashboard",
+            },
+          ],
+        },
+      },
+      ...sub_routes,
+    ],
+  },
+  {
+    path: "/login",
+    component: () => import("layouts/LoginLayout.vue"),
+    children: [
+      {
+        path: "",
+        name: "LoginPage",
+        component: () => import("pages/LoginPage.vue"),
+        meta: {
+          title: "Login",
+        },
+      },
+    ],
+  },
+
+  // Always leave this as last one,
+  // but you can also remove it
+  {
+    path: "/:catchAll(.*)*",
+    component: () => import("pages/ErrorNotFound.vue"),
+  },
+];
+
+export default routes;

+ 62 - 0
src/router/routes/regions.route.js

@@ -0,0 +1,62 @@
+export default [
+  {
+    path: "/city",
+    name: "CityPage",
+    component: () => import("pages/city/CityPage.vue"),
+    meta: {
+      title: "ui.navigation.city",
+      requireAuth: true,
+      requiredPermission: "config.city",
+      breadcrumbs: [
+        {
+          name: "DashboardPage",
+          title: "ui.navigation.dashboard",
+        },
+        {
+          name: "CityPage",
+          title: "ui.navigation.city",
+        },
+      ],
+    },
+  },
+  {
+    path: "/country",
+    name: "CountryPage",
+    component: () => import("pages/country/CountryPage.vue"),
+    meta: {
+      title: "ui.navigation.country",
+      requireAuth: true,
+      requiredPermission: "config.country",
+      breadcrumbs: [
+        {
+          name: "DashboardPage",
+          title: "ui.navigation.dashboard",
+        },
+        {
+          name: "CountryPage",
+          title: "ui.navigation.country",
+        },
+      ],
+    },
+  },
+  {
+    path: "/state",
+    name: "StatePage",
+    component: () => import("pages/state/StatePage.vue"),
+    meta: {
+      title: "ui.navigation.state",
+      requireAuth: true,
+      requiredPermission: "config.state",
+      breadcrumbs: [
+        {
+          name: "DashboardPage",
+          title: "ui.navigation.dashboard",
+        },
+        {
+          name: "StatePage",
+          title: "ui.navigation.state",
+        },
+      ],
+    },
+  },
+]

+ 22 - 0
src/router/routes/users.route.js

@@ -0,0 +1,22 @@
+export default [
+  {
+    path: "/users",
+    name: "UsersPage",
+    component: () => import("pages/users/UsersPage.vue"),
+    meta: {
+      title: "ui.navigation.users",
+      requireAuth: true,
+      requiredPermission: "config.user",
+      breadcrumbs: [
+        {
+          name: "DashboardPage",
+          title: "ui.navigation.dashboard",
+        },
+        {
+          name: "UsersPage",
+          title: "ui.navigation.users",
+        },
+      ],
+    },
+  },
+];

+ 20 - 0
src/stores/index.js

@@ -0,0 +1,20 @@
+import { defineStore } from '#q-app/wrappers'
+import { createPinia } from 'pinia'
+
+/*
+ * If not building with SSR mode, you can
+ * directly export the Store instantiation;
+ *
+ * The function below can be async too; either use
+ * async/await or return a Promise which resolves
+ * with the Store instance.
+ */
+
+export default defineStore((/* { ssrContext } */) => {
+  const pinia = createPinia()
+
+  // You can add Pinia plugins here
+  // pinia.use(SomePiniaPlugin)
+
+  return pinia
+})

+ 90 - 0
src/stores/navigation.js

@@ -0,0 +1,90 @@
+import { defineStore } from "pinia";
+import { computed } from "vue";
+import { permissionStore } from "src/stores/permission";
+
+export const navigationStore = defineStore("navigation", () => {
+  const navigationStructure = Object.freeze([
+    {
+      type: "single",
+      title: "ui.navigation.dashboard",
+      name: "DashboardPage",
+      icon: "mdi-home-variant-outline",
+      disable: false,
+      permission: false,
+      permissionScope: "dashboard",
+    },
+    {
+      type: "expansive",
+      title: "ui.navigation.registration",
+      icon: "mdi-cog-outline",
+      disable: false,
+      permission: false,
+      permissionScope: "config",
+      childrens: [
+        {
+          type: "single",
+          title: "ui.navigation.users",
+          name: "UsersPage",
+          icon: "mdi-account-multiple-outline",
+          disable: false,
+          permission: false,
+          permissionScope: "config.user",
+        },
+        {
+          type: "single",
+          title: "ui.navigation.city",
+          name: "CityPage",
+          icon: "mdi-city-variant-outline",
+          disable: false,
+          permission: false,
+          permissionScope: "config.city",
+        },
+        {
+          type: "single",
+          title: "ui.navigation.country",
+          name: "CountryPage",
+          icon: "mdi-earth",
+          disable: false,
+          permission: false,
+          permissionScope: "config.country",
+        },
+        {
+          type: "single",
+          title: "ui.navigation.state",
+          name: "StatePage",
+          icon: "mdi-map-marker",
+          disable: false,
+          permission: false,
+          permissionScope: "config.state",
+        },
+      ],
+    },
+  ]);
+
+  const getNavigationAccess = () => {
+    const { getAccess } = permissionStore();
+    return navigationStructure
+      .map((menu) => {
+        if (menu.type === "expansive") {
+          if (getAccess(menu.permissionScope, "menu")) {
+            menu.permission = true;
+          }
+          menu.childrens = menu.childrens.filter((children) => {
+            children.permission = getAccess(children.permissionScope, "menu");
+            return children.permission;
+          });
+          return menu.childrens.length > 0 ? menu : null;
+        } else {
+          menu.permission = getAccess(menu.permissionScope, "menu");
+          return menu;
+        }
+      })
+      .filter((menu) => menu !== null);
+  };
+
+  const navigationItems = computed(() => getNavigationAccess());
+
+  return {
+    navigationItems,
+  };
+});

+ 126 - 0
src/stores/permission.js

@@ -0,0 +1,126 @@
+import { defineStore } from "pinia";
+import { ref, computed } from "vue";
+import { userStore } from "src/stores/user";
+import { getUserPermissions, getGuestPermissions } from "src/api/permission";
+
+export const permissionStore = defineStore("permission", () => {
+  const bitwisePermissionTable = Object.freeze({
+    view: 1,
+    add: 2,
+    edit: 4,
+    delete: 8,
+    print: 16,
+    export: 32,
+    import: 64,
+    limit: 128,
+    menu: 256,
+  });
+
+  const bitwisePermissions = ref({
+    view: 0,
+    add: 0,
+    edit: 0,
+    delete: 0,
+    print: 0,
+    export: 0,
+    import: 0,
+    limit: 0,
+    menu: 0,
+  });
+
+  const originalBitwisePermissions = ref(null);
+  const permissions = ref(null);
+
+  const totalBitwisePermissions = computed(() =>
+    Object.values(bitwisePermissionTable).reduce((a, b) => a + b),
+  );
+
+  const checkTotalPermissions = (permission) => {
+    return !!(permission & totalBitwisePermissions.value);
+  };
+
+  const checkPermission = (permission, total) => {
+    return !!(permission & total);
+  };
+
+  const updateBitMasks = (total) => {
+    const totalPermissions = totalBitwisePermissions.value;
+
+    const setPermission = (permissionKey, permissionValue) => {
+      bitwisePermissions.value[permissionKey] =
+        total & permissionValue ? permissionValue : 0;
+    };
+
+    if (total & totalPermissions) {
+      setPermission("view", bitwisePermissionTable.view);
+      setPermission("add", bitwisePermissionTable.add);
+      setPermission("edit", bitwisePermissionTable.edit);
+      setPermission("delete", bitwisePermissionTable.delete);
+      setPermission("print", bitwisePermissionTable.print);
+      setPermission("export", bitwisePermissionTable.export);
+      setPermission("import", bitwisePermissionTable.import);
+      setPermission("limit", bitwisePermissionTable.limit);
+      setPermission("menu", bitwisePermissionTable.menu);
+
+      originalBitwisePermissions.value = { ...bitwisePermissions.value };
+    } else {
+      for (let key in bitwisePermissions.value) {
+        bitwisePermissions.value[key] = 0;
+      }
+      originalBitwisePermissions.value = { ...bitwisePermissions.value };
+    }
+  };
+
+  const getAccess = (scopeName, permissionType) => {
+    const { isAdmin } = userStore();
+
+    if (isAdmin) {
+      return true;
+    }
+
+    if (permissions.value) {
+      let checkPermission = 0;
+      const scope = permissions.value.find(
+        (permission) => permission.scope === scopeName,
+      );
+
+      if (scope) {
+        checkPermission = bitwisePermissionTable[permissionType] || 0;
+        return scope.bits & checkPermission ? true : false;
+      }
+    }
+    return false;
+  };
+
+  const fetchScopes = async () => {
+    try {
+      const { accessToken } = userStore();
+      if (accessToken) {
+        const userPermissions = await getUserPermissions();
+        permissions.value = userPermissions;
+      } else {
+        const response = await getGuestPermissions();
+        permissions.value = response;
+      }
+    } catch (error) {
+      console.error("Error fetching permissions:", error);
+    }
+  };
+
+  const resetScopes = () => {
+    permissions.value = null;
+  };
+
+  return {
+    bitwisePermissions,
+    originalBitwisePermissions,
+    totalBitwisePermissions,
+    checkTotalPermissions,
+    checkPermission,
+    updateBitMasks,
+    permissions,
+    getAccess,
+    fetchScopes,
+    resetScopes,
+  };
+});

+ 10 - 0
src/stores/store-flag.d.ts

@@ -0,0 +1,10 @@
+/* eslint-disable */
+// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
+//  REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
+import "quasar/dist/types/feature-flag";
+
+declare module "quasar/dist/types/feature-flag" {
+  interface QuasarFeatureFlags {
+    store: true;
+  }
+}

+ 34 - 0
src/stores/user.js

@@ -0,0 +1,34 @@
+import { defineStore } from "pinia";
+import { ref } from "vue";
+import { getUser } from "src/api/user";
+
+export const userStore = defineStore("user", () => {
+  const user = ref(null);
+  const accessToken = ref(null);
+  const isAdmin = ref(false);
+
+  const setUser = (userData) => {
+    user.value = userData;
+    isAdmin.value = userData.type === "admin";
+  };
+
+  const resetUser = () => {
+    user.value = null;
+    isAdmin.value = false;
+    accessToken.value = false;
+  };
+
+  const fetchUser = async () => {
+    const response = await getUser();
+    setUser(response);
+  };
+
+  return {
+    user,
+    isAdmin,
+    setUser,
+    resetUser,
+    fetchUser,
+    accessToken,
+  };
+});

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно