From 69640da28ec3da1412353a7f37e48797831615aa Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Tue, 29 Oct 2024 19:53:28 +0300
Subject: [PATCH 01/37] Add basic di in client side

---
 .eslintrc.json                           |   10 +
 package.json                             |   12 +-
 src/app/layout.tsx                       |    2 +-
 src/app/test/page.tsx                    |    8 +
 src/app/test/vm/test-button-vm.ts        |   17 +
 src/bootstrap/di/init-di.ts              |   18 +
 src/bootstrap/helpers/type-helper.ts     |    8 +
 src/bootstrap/helpers/view/base-view.tsx |   95 +
 src/bootstrap/helpers/vm/base-vm.ts      |   46 +
 src/bootstrap/helpers/vm/i-base-vm.ts    |    3 +
 src/bootstrap/helpers/vm/vm-decorator.ts |    9 +
 src/components/button/button-vm.ts       |    6 +
 src/components/button/button.tsx         |   10 +
 tsconfig.json                            |    2 +
 yarn.lock                                | 2879 ++++++++++++++++++++++
 15 files changed, 3119 insertions(+), 6 deletions(-)
 create mode 100644 src/app/test/page.tsx
 create mode 100644 src/app/test/vm/test-button-vm.ts
 create mode 100644 src/bootstrap/di/init-di.ts
 create mode 100644 src/bootstrap/helpers/type-helper.ts
 create mode 100644 src/bootstrap/helpers/view/base-view.tsx
 create mode 100644 src/bootstrap/helpers/vm/base-vm.ts
 create mode 100644 src/bootstrap/helpers/vm/i-base-vm.ts
 create mode 100644 src/bootstrap/helpers/vm/vm-decorator.ts
 create mode 100644 src/components/button/button-vm.ts
 create mode 100644 src/components/button/button.tsx
 create mode 100644 yarn.lock

diff --git a/.eslintrc.json b/.eslintrc.json
index 3722418..60d067f 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,3 +1,13 @@
 {
+  "overrides": [
+    {
+      "files": [
+        "src/**/*-vm.ts"
+      ],
+      "rules": {
+          "react-hooks/rules-of-hooks": "off"
+      }
+    }
+  ],
   "extends": ["next/core-web-vitals", "next/typescript"]
 }
diff --git a/package.json b/package.json
index d10c385..2db330d 100644
--- a/package.json
+++ b/package.json
@@ -5,22 +5,24 @@
   "scripts": {
     "dev": "next dev --turbopack",
     "build": "next build",
-    "start": "next start",
+    "start": "next start --port 4000",
     "lint": "next lint"
   },
   "dependencies": {
+    "next": "15.0.1",
     "react": "19.0.0-rc-69d4b800-20241021",
     "react-dom": "19.0.0-rc-69d4b800-20241021",
-    "next": "15.0.1"
+    "reflect-metadata": "^0.2.2",
+    "tsyringe": "^4.8.0"
   },
   "devDependencies": {
-    "typescript": "^5",
     "@types/node": "^20",
     "@types/react": "^18",
     "@types/react-dom": "^18",
+    "eslint": "^8",
+    "eslint-config-next": "15.0.1",
     "postcss": "^8",
     "tailwindcss": "^3.4.1",
-    "eslint": "^8",
-    "eslint-config-next": "15.0.1"
+    "typescript": "^5"
   }
 }
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index a36cde0..1cd9211 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,7 +1,7 @@
+import "reflect-metadata"
 import type { Metadata } from "next";
 import localFont from "next/font/local";
 import "./globals.css";
-
 const geistSans = localFont({
   src: "./fonts/GeistVF.woff",
   variable: "--font-geist-sans",
diff --git a/src/app/test/page.tsx b/src/app/test/page.tsx
new file mode 100644
index 0000000..f08fc5e
--- /dev/null
+++ b/src/app/test/page.tsx
@@ -0,0 +1,8 @@
+"use client"
+import "reflect-metadata"
+import TestButtonVM from "@/app/test/vm/test-button-vm";
+import Button from "@/components/button/button";
+
+export default function Page() {
+    return <Button vmName={TestButtonVM.name} />
+}
\ No newline at end of file
diff --git a/src/app/test/vm/test-button-vm.ts b/src/app/test/vm/test-button-vm.ts
new file mode 100644
index 0000000..414d5ce
--- /dev/null
+++ b/src/app/test/vm/test-button-vm.ts
@@ -0,0 +1,17 @@
+import BaseVM from "@/bootstrap/helpers/vm/base-vm";
+import injectableVm from "@/bootstrap/helpers/vm/vm-decorator";
+import ButtonVm from "@/components/button/button-vm";
+
+@injectableVm()
+export default class TestButtonVM extends BaseVM<ButtonVm> {
+    useVM(): ButtonVm {
+        return {
+            props: {
+                title: "Test Button"
+            },
+            onClick: () => {
+                console.log("clicked on the button");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/bootstrap/di/init-di.ts b/src/bootstrap/di/init-di.ts
new file mode 100644
index 0000000..79281ec
--- /dev/null
+++ b/src/bootstrap/di/init-di.ts
@@ -0,0 +1,18 @@
+import "reflect-metadata"
+
+import { container, DependencyContainer } from "tsyringe";
+
+/**
+ * Serves as a central point for initializing and configuring
+ *  the DI container, ensuring that all necessary dependencies
+ *  are registered and available for injection throughout the application.
+ */
+const InitDI = (): DependencyContainer => {
+  const di = container;
+
+  return di;
+};
+
+const di = InitDI();
+
+export default di;
\ No newline at end of file
diff --git a/src/bootstrap/helpers/type-helper.ts b/src/bootstrap/helpers/type-helper.ts
new file mode 100644
index 0000000..c484e86
--- /dev/null
+++ b/src/bootstrap/helpers/type-helper.ts
@@ -0,0 +1,8 @@
+declare const _: unique symbol;
+
+type Forbidden = { [_]: typeof _ };
+
+/**
+ * You can use this type to make your parent class method forbidden to overwrite
+ */
+export type NoOverride<T = void> = T & Forbidden;
\ No newline at end of file
diff --git a/src/bootstrap/helpers/view/base-view.tsx b/src/bootstrap/helpers/view/base-view.tsx
new file mode 100644
index 0000000..a7498cf
--- /dev/null
+++ b/src/bootstrap/helpers/view/base-view.tsx
@@ -0,0 +1,95 @@
+"use client"
+/* eslint-disable react/display-name */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable react/jsx-props-no-spreading */
+import di from "@/bootstrap/di/init-di";
+import BaseVM from "@/bootstrap/helpers/vm/base-vm";
+import { Component, ReactNode, FC, PropsWithChildren, memo, MemoExoticComponent } from "react";
+
+/* -------------------------------------------------------------------------- */
+/*                             Connector Component                            */
+/* -------------------------------------------------------------------------- */
+interface IVvmConnector<IVM, PROPS> extends PropsWithChildren {
+  View: FC<any & { vm: IVM }>;
+  vmName: string;
+  restProps?: PROPS;
+  memoizedByVM?: boolean;
+}
+
+/**
+ * This function is just will be used in
+ */
+const VvmConnector = memo(
+  <IVM, PROPS>(props: IVvmConnector<IVM, PROPS>) => {
+    const { View, vmName, restProps, children } = props;
+    const VmInstance = di.resolve(vmName) as new () => BaseVM<IVM>;
+    if (!VmInstance) throw new Error(`Provided vm as ${vmName} is not exists`)
+
+    const vm = new VmInstance().useVM()
+
+    const allProps = {
+      restProps,
+      vm,
+    };
+
+    return <View {...allProps}>{children}</View>;
+  },
+  (prevProps) => {
+    if (prevProps.memoizedByVM) return true;
+    return false;
+  },
+);
+
+/* -------------------------------------------------------------------------- */
+/*                                  BaseView                                  */
+/* -------------------------------------------------------------------------- */
+type IVMParent = Record<string, any>;
+type IPropParent = Record<string, any> | undefined;
+
+type BaseProps<PROPS extends IPropParent = undefined> = {
+  vmName: string;
+  restProps?: PROPS;
+  /**
+   * By default it's true.
+   * If you pass true this view will update just by changes of vm not rest props
+   *
+   */
+  memoizedByVM?: boolean;
+  children?: ReactNode;
+};
+
+export type BuildProps<
+  IVM extends IVMParent,
+  PROPS extends IPropParent = undefined,
+> = {
+  vm: IVM;
+  restProps: PROPS;
+  children?: ReactNode;
+};
+
+export default abstract class BaseView<
+  IVM extends IVMParent,
+  PROPS extends IPropParent = undefined,
+> extends Component<BaseProps<PROPS>> {
+  /* -------------------------------- Abstracts ------------------------------- */
+  protected abstract Build(props: BuildProps<IVM, PROPS>): ReactNode;
+
+  /* -------------------------------- Renderer -------------------------------- */
+  render(): ReactNode {
+    const { vmName, restProps, memoizedByVM, children, ...rest } = this.props;
+    
+    const Connector = VvmConnector as MemoExoticComponent<((props: IVvmConnector<IVM, PROPS>) => JSX.Element)>;
+
+    return (
+      <Connector
+        View={this.Build}
+        vmName={vmName}
+        memoizedByVM={typeof memoizedByVM === "undefined" ? true : memoizedByVM}
+        restProps={{ ...restProps, ...rest } as PROPS}
+      >
+        {children}
+      </Connector>
+    );
+  }
+  /* -------------------------------------------------------------------------- */
+}
diff --git a/src/bootstrap/helpers/vm/base-vm.ts b/src/bootstrap/helpers/vm/base-vm.ts
new file mode 100644
index 0000000..e82a80e
--- /dev/null
+++ b/src/bootstrap/helpers/vm/base-vm.ts
@@ -0,0 +1,46 @@
+"use client"
+import { NoOverride } from "@/bootstrap/helpers/type-helper";
+import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
+import { useState } from "react";
+
+export default abstract class BaseVM<
+  IVM,
+  DEP extends object | undefined = undefined,
+> implements IBaseVM<IVM>
+{
+  /* ------------------------------ Dependencies ------------------------------ */
+  protected deps!: DEP;
+
+  /* -------------------------------- Abstracts ------------------------------- */
+  abstract useVM(): IVM;
+
+  /* -------------------------------------------------------------------------- */
+  produce(dep?: DEP) {
+    if (dep) this.deps = dep;
+
+    return this;
+  }
+
+  /* --------------------------------- Getters -------------------------------- */
+  /**
+   * You can pass your rerender method after calling useRerender on your vm
+   *  so you can access to it in any method
+   */
+  protected rerender?: () => void;
+
+  /* -------------------------------------------------------------------------- */
+  /**
+   * You can use this hook in your useVm method to get rerender method
+   * @returns Rerender Method that when ever you call it you can rerender your component
+   *  for showing new values
+   */
+  protected useRerender(): NoOverride<() => void> {
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    const [_, reState] = useState(false);
+
+    const rerender = () => reState((prev) => !prev);
+    return rerender as NoOverride<() => void>;
+  }
+
+  /* -------------------------------------------------------------------------- */
+}
diff --git a/src/bootstrap/helpers/vm/i-base-vm.ts b/src/bootstrap/helpers/vm/i-base-vm.ts
new file mode 100644
index 0000000..0e474fa
--- /dev/null
+++ b/src/bootstrap/helpers/vm/i-base-vm.ts
@@ -0,0 +1,3 @@
+export default interface IBaseVM<VM> {
+  useVM(): VM;
+}
diff --git a/src/bootstrap/helpers/vm/vm-decorator.ts b/src/bootstrap/helpers/vm/vm-decorator.ts
new file mode 100644
index 0000000..2092738
--- /dev/null
+++ b/src/bootstrap/helpers/vm/vm-decorator.ts
@@ -0,0 +1,9 @@
+"use client"
+import di from "@/bootstrap/di/init-di";
+import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
+
+export default function injectableVm() {
+    return function (target: new (...args: unknown[]) => IBaseVM<object>) {
+        di.registerInstance(target.name, target);
+    };
+}
\ No newline at end of file
diff --git a/src/components/button/button-vm.ts b/src/components/button/button-vm.ts
new file mode 100644
index 0000000..f9e8754
--- /dev/null
+++ b/src/components/button/button-vm.ts
@@ -0,0 +1,6 @@
+export default interface ButtonVm {
+    props: {
+        title: string
+    }
+    onClick(): void
+}
\ No newline at end of file
diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx
new file mode 100644
index 0000000..6b0452b
--- /dev/null
+++ b/src/components/button/button.tsx
@@ -0,0 +1,10 @@
+import BaseView, { BuildProps } from "@/bootstrap/helpers/view/base-view";
+import ButtonVm from "@/components/button/button-vm";
+import { ReactNode } from "react";
+
+export default class Button extends BaseView<ButtonVm> {
+    protected Build(props: BuildProps<ButtonVm>): ReactNode {
+        const {vm} = props
+        return <button onClick={vm.onClick} >{vm.props.title}</button>
+    }
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index c133409..714376b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,6 +9,8 @@
     "esModuleInterop": true,
     "module": "esnext",
     "moduleResolution": "bundler",
+    "experimentalDecorators": true,
+    "emitDecoratorMetadata": true,
     "resolveJsonModule": true,
     "isolatedModules": true,
     "jsx": "preserve",
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..05b8294
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,2879 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@alloc/quick-lru@^5.2.0":
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
+  integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
+
+"@emnapi/runtime@^1.2.0":
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60"
+  integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==
+  dependencies:
+    tslib "^2.4.0"
+
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56"
+  integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==
+  dependencies:
+    eslint-visitor-keys "^3.4.3"
+
+"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
+  version "4.12.1"
+  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
+  integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
+
+"@eslint/eslintrc@^2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
+  integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.3.2"
+    espree "^9.6.0"
+    globals "^13.19.0"
+    ignore "^5.2.0"
+    import-fresh "^3.2.1"
+    js-yaml "^4.1.0"
+    minimatch "^3.1.2"
+    strip-json-comments "^3.1.1"
+
+"@eslint/js@8.57.1":
+  version "8.57.1"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
+  integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
+
+"@humanwhocodes/config-array@^0.13.0":
+  version "0.13.0"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748"
+  integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==
+  dependencies:
+    "@humanwhocodes/object-schema" "^2.0.3"
+    debug "^4.3.1"
+    minimatch "^3.0.5"
+
+"@humanwhocodes/module-importer@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+  integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/object-schema@^2.0.3":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
+  integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
+
+"@img/sharp-darwin-arm64@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08"
+  integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==
+  optionalDependencies:
+    "@img/sharp-libvips-darwin-arm64" "1.0.4"
+
+"@img/sharp-darwin-x64@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61"
+  integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==
+  optionalDependencies:
+    "@img/sharp-libvips-darwin-x64" "1.0.4"
+
+"@img/sharp-libvips-darwin-arm64@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f"
+  integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==
+
+"@img/sharp-libvips-darwin-x64@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062"
+  integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==
+
+"@img/sharp-libvips-linux-arm64@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704"
+  integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==
+
+"@img/sharp-libvips-linux-arm@1.0.5":
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197"
+  integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==
+
+"@img/sharp-libvips-linux-s390x@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce"
+  integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==
+
+"@img/sharp-libvips-linux-x64@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0"
+  integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==
+
+"@img/sharp-libvips-linuxmusl-arm64@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5"
+  integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==
+
+"@img/sharp-libvips-linuxmusl-x64@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff"
+  integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==
+
+"@img/sharp-linux-arm64@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22"
+  integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==
+  optionalDependencies:
+    "@img/sharp-libvips-linux-arm64" "1.0.4"
+
+"@img/sharp-linux-arm@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff"
+  integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==
+  optionalDependencies:
+    "@img/sharp-libvips-linux-arm" "1.0.5"
+
+"@img/sharp-linux-s390x@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667"
+  integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==
+  optionalDependencies:
+    "@img/sharp-libvips-linux-s390x" "1.0.4"
+
+"@img/sharp-linux-x64@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb"
+  integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==
+  optionalDependencies:
+    "@img/sharp-libvips-linux-x64" "1.0.4"
+
+"@img/sharp-linuxmusl-arm64@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b"
+  integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==
+  optionalDependencies:
+    "@img/sharp-libvips-linuxmusl-arm64" "1.0.4"
+
+"@img/sharp-linuxmusl-x64@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48"
+  integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==
+  optionalDependencies:
+    "@img/sharp-libvips-linuxmusl-x64" "1.0.4"
+
+"@img/sharp-wasm32@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1"
+  integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==
+  dependencies:
+    "@emnapi/runtime" "^1.2.0"
+
+"@img/sharp-win32-ia32@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9"
+  integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==
+
+"@img/sharp-win32-x64@0.33.5":
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342"
+  integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==
+
+"@isaacs/cliui@^8.0.2":
+  version "8.0.2"
+  resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
+  integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
+  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"
+
+"@jridgewell/gen-mapping@^0.3.2":
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
+  integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
+  dependencies:
+    "@jridgewell/set-array" "^1.2.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/resolve-uri@^3.1.0":
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+  integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
+"@jridgewell/set-array@^1.2.1":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
+  integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
+
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
+  integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
+
+"@jridgewell/trace-mapping@^0.3.24":
+  version "0.3.25"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
+  integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.1.0"
+    "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@next/env@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/env/-/env-15.0.1.tgz#660fe9303e255cec112d3f4198d2897a24bc60b3"
+  integrity sha512-lc4HeDUKO9gxxlM5G2knTRifqhsY6yYpwuHspBZdboZe0Gp+rZHBNNSIjmQKDJIdRXiXGyVnSD6gafrbQPvILQ==
+
+"@next/eslint-plugin-next@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.1.tgz#76117d88aadc52f6e04b1892d44654d05468d53c"
+  integrity sha512-bKWsMaGPbiFAaGqrDJvbE8b4Z0uKicGVcgOI77YM2ui3UfjHMr4emFPrZTLeZVchi7fT1mooG2LxREfUUClIKw==
+  dependencies:
+    fast-glob "3.3.1"
+
+"@next/swc-darwin-arm64@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.1.tgz#b80a25f1569bd0ca03eca9473f7e93e64937e404"
+  integrity sha512-C9k/Xv4sxkQRTA37Z6MzNq3Yb1BJMmSqjmwowoWEpbXTkAdfOwnoKOpAb71ItSzoA26yUTIo6ZhN8rKGu4ExQw==
+
+"@next/swc-darwin-x64@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.1.tgz#00dcf79ec7c638a85c3b9ff2e2de2bfb09c1c250"
+  integrity sha512-uHl13HXOuq1G7ovWFxCACDJHTSDVbn/sbLv8V1p+7KIvTrYQ5HNoSmKBdYeEKRRCbEmd+OohOgg9YOp8Ux3MBg==
+
+"@next/swc-linux-arm64-gnu@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.1.tgz#faab5f7ffcc6d1a15e8dea1cb9953966658b39bf"
+  integrity sha512-LvyhvxHOihFTEIbb35KxOc3q8w8G4xAAAH/AQnsYDEnOvwawjL2eawsB59AX02ki6LJdgDaHoTEnC54Gw+82xw==
+
+"@next/swc-linux-arm64-musl@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.1.tgz#97abada9a782ab5b3cb42cf0d4799cbc2e733351"
+  integrity sha512-vFmCGUFNyk/A5/BYcQNhAQqPIw01RJaK6dRO+ZEhz0DncoW+hJW1kZ8aH2UvTX27zPq3m85zN5waMSbZEmANcQ==
+
+"@next/swc-linux-x64-gnu@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.1.tgz#548bd47c49fe6d819302139aff8766eb704322e2"
+  integrity sha512-5by7IYq0NCF8rouz6Qg9T97jYU68kaClHPfGpQG2lCZpSYHtSPQF1kjnqBTd34RIqPKMbCa4DqCufirgr8HM5w==
+
+"@next/swc-linux-x64-musl@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.1.tgz#84423fbd3a058dd6ae8322e530878f0ec7a1027a"
+  integrity sha512-lmYr6H3JyDNBJLzklGXLfbehU3ay78a+b6UmBGlHls4xhDXBNZfgb0aI67sflrX+cGBnv1LgmWzFlYrAYxS1Qw==
+
+"@next/swc-win32-arm64-msvc@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.1.tgz#723c2ced12a998fb40dc901b8faea9170e788c2f"
+  integrity sha512-DS8wQtl6diAj0eZTdH0sefykm4iXMbHT4MOvLwqZiIkeezKpkgPFcEdFlz3vKvXa2R/2UEgMh48z1nEpNhjeOQ==
+
+"@next/swc-win32-x64-msvc@15.0.1":
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.1.tgz#ec7e3befc0bcc47527537b1eda2b3745beb15a09"
+  integrity sha512-4Ho2ggvDdMKlZ/0e9HNdZ9ngeaBwtc+2VS5oCeqrbXqOgutX6I4U2X/42VBw0o+M5evn4/7v3zKgGHo+9v/VjA==
+
+"@nodelib/fs.scandir@2.1.5":
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+  dependencies:
+    "@nodelib/fs.stat" "2.0.5"
+    run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.5"
+    fastq "^1.6.0"
+
+"@nolyfill/is-core-module@1.0.39":
+  version "1.0.39"
+  resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e"
+  integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==
+
+"@pkgjs/parseargs@^0.11.0":
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
+  integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
+
+"@rtsao/scc@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
+  integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
+
+"@rushstack/eslint-patch@^1.10.3":
+  version "1.10.4"
+  resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1"
+  integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==
+
+"@swc/counter@0.1.3":
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9"
+  integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
+
+"@swc/helpers@0.5.13":
+  version "0.5.13"
+  resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.13.tgz#33e63ff3cd0cade557672bd7888a39ce7d115a8c"
+  integrity sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==
+  dependencies:
+    tslib "^2.4.0"
+
+"@types/json5@^0.0.29":
+  version "0.0.29"
+  resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
+  integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
+
+"@types/node@^20":
+  version "20.17.2"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.2.tgz#3ca40ef7d776c85a1db3df23cbb5bfb3c384a92e"
+  integrity sha512-OOHK4sjXqkL7yQ7VEEHcf6+0jSvKjWqwnaCtY7AKD/VLEvRHMsxxu7eI8ErnjxHS8VwmekD4PeVCpu4qZEZSxg==
+  dependencies:
+    undici-types "~6.19.2"
+
+"@types/prop-types@*":
+  version "15.7.13"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451"
+  integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==
+
+"@types/react-dom@^18":
+  version "18.3.1"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07"
+  integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react@*", "@types/react@^18":
+  version "18.3.12"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60"
+  integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==
+  dependencies:
+    "@types/prop-types" "*"
+    csstype "^3.0.2"
+
+"@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
+  version "8.12.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz#c2ef660bb83fd1432368319312a2581fc92ccac1"
+  integrity sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==
+  dependencies:
+    "@eslint-community/regexpp" "^4.10.0"
+    "@typescript-eslint/scope-manager" "8.12.2"
+    "@typescript-eslint/type-utils" "8.12.2"
+    "@typescript-eslint/utils" "8.12.2"
+    "@typescript-eslint/visitor-keys" "8.12.2"
+    graphemer "^1.4.0"
+    ignore "^5.3.1"
+    natural-compare "^1.4.0"
+    ts-api-utils "^1.3.0"
+
+"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
+  version "8.12.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.12.2.tgz#2e8173b34e1685e918b2d571c16c906d3747bad2"
+  integrity sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==
+  dependencies:
+    "@typescript-eslint/scope-manager" "8.12.2"
+    "@typescript-eslint/types" "8.12.2"
+    "@typescript-eslint/typescript-estree" "8.12.2"
+    "@typescript-eslint/visitor-keys" "8.12.2"
+    debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@8.12.2":
+  version "8.12.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz#6db0213745e6392c8e90fe9af5915e6da32eb94a"
+  integrity sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==
+  dependencies:
+    "@typescript-eslint/types" "8.12.2"
+    "@typescript-eslint/visitor-keys" "8.12.2"
+
+"@typescript-eslint/type-utils@8.12.2":
+  version "8.12.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz#132b0c52d45f6814e6f2e32416c7951ed480b016"
+  integrity sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==
+  dependencies:
+    "@typescript-eslint/typescript-estree" "8.12.2"
+    "@typescript-eslint/utils" "8.12.2"
+    debug "^4.3.4"
+    ts-api-utils "^1.3.0"
+
+"@typescript-eslint/types@8.12.2":
+  version "8.12.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.12.2.tgz#8d70098c0e90442495b53d0296acdca6d0f3f73c"
+  integrity sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==
+
+"@typescript-eslint/typescript-estree@8.12.2":
+  version "8.12.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz#206df9b1cbff212aaa9401985ef99f04daa84da5"
+  integrity sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==
+  dependencies:
+    "@typescript-eslint/types" "8.12.2"
+    "@typescript-eslint/visitor-keys" "8.12.2"
+    debug "^4.3.4"
+    fast-glob "^3.3.2"
+    is-glob "^4.0.3"
+    minimatch "^9.0.4"
+    semver "^7.6.0"
+    ts-api-utils "^1.3.0"
+
+"@typescript-eslint/utils@8.12.2":
+  version "8.12.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.12.2.tgz#726cc9f49f5866605bd15bbc1768ffc15637930e"
+  integrity sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.4.0"
+    "@typescript-eslint/scope-manager" "8.12.2"
+    "@typescript-eslint/types" "8.12.2"
+    "@typescript-eslint/typescript-estree" "8.12.2"
+
+"@typescript-eslint/visitor-keys@8.12.2":
+  version "8.12.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz#94d7410f78eb6d134b9fcabaf1eeedb910ba8c38"
+  integrity sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==
+  dependencies:
+    "@typescript-eslint/types" "8.12.2"
+    eslint-visitor-keys "^3.4.3"
+
+"@ungap/structured-clone@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
+  integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+
+acorn-jsx@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.9.0:
+  version "8.14.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
+  integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
+
+ajv@^6.12.4:
+  version "6.12.6"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+  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"
+
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-regex@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
+  integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+ansi-styles@^6.1.0:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+  integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
+any-promise@^1.0.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
+  integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
+
+anymatch@~3.1.2:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+  integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
+arg@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
+  integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
+
+argparse@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+aria-query@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59"
+  integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==
+
+array-buffer-byte-length@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f"
+  integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==
+  dependencies:
+    call-bind "^1.0.5"
+    is-array-buffer "^3.0.4"
+
+array-includes@^3.1.6, array-includes@^3.1.8:
+  version "3.1.8"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d"
+  integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.2"
+    es-object-atoms "^1.0.0"
+    get-intrinsic "^1.2.4"
+    is-string "^1.0.7"
+
+array.prototype.findlast@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904"
+  integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.2"
+    es-errors "^1.3.0"
+    es-object-atoms "^1.0.0"
+    es-shim-unscopables "^1.0.2"
+
+array.prototype.findlastindex@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d"
+  integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.2"
+    es-errors "^1.3.0"
+    es-object-atoms "^1.0.0"
+    es-shim-unscopables "^1.0.2"
+
+array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18"
+  integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+    es-shim-unscopables "^1.0.0"
+
+array.prototype.flatmap@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527"
+  integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+    es-shim-unscopables "^1.0.0"
+
+array.prototype.tosorted@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc"
+  integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.3"
+    es-errors "^1.3.0"
+    es-shim-unscopables "^1.0.2"
+
+arraybuffer.prototype.slice@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6"
+  integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==
+  dependencies:
+    array-buffer-byte-length "^1.0.1"
+    call-bind "^1.0.5"
+    define-properties "^1.2.1"
+    es-abstract "^1.22.3"
+    es-errors "^1.2.1"
+    get-intrinsic "^1.2.3"
+    is-array-buffer "^3.0.4"
+    is-shared-array-buffer "^1.0.2"
+
+ast-types-flow@^0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6"
+  integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==
+
+available-typed-arrays@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
+  integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==
+  dependencies:
+    possible-typed-array-names "^1.0.0"
+
+axe-core@^4.10.0:
+  version "4.10.2"
+  resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df"
+  integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==
+
+axobject-query@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee"
+  integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==
+
+balanced-match@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+binary-extensions@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
+  integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+brace-expansion@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+  integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+  dependencies:
+    balanced-match "^1.0.0"
+
+braces@^3.0.3, braces@~3.0.2:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+  integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+  dependencies:
+    fill-range "^7.1.1"
+
+busboy@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
+  integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
+  dependencies:
+    streamsearch "^1.1.0"
+
+call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
+  integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
+  dependencies:
+    es-define-property "^1.0.0"
+    es-errors "^1.3.0"
+    function-bind "^1.1.2"
+    get-intrinsic "^1.2.4"
+    set-function-length "^1.2.1"
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camelcase-css@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
+  integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
+
+caniuse-lite@^1.0.30001579:
+  version "1.0.30001674"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001674.tgz#eb200a716c3e796d33d30b9c8890517a72f862c8"
+  integrity sha512-jOsKlZVRnzfhLojb+Ykb+gyUSp9Xb57So+fAiFlLzzTKpqg8xxSav0e40c8/4F/v9N8QSvrRRaLeVzQbLqomYw==
+
+chalk@^4.0.0:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chokidar@^3.5.3:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
+  integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
+  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"
+  optionalDependencies:
+    fsevents "~2.3.2"
+
+client-only@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
+  integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@^1.0.0, color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-string@^1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
+  integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
+  dependencies:
+    color-name "^1.0.0"
+    simple-swizzle "^0.2.2"
+
+color@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
+  integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
+  dependencies:
+    color-convert "^2.0.1"
+    color-string "^1.9.0"
+
+commander@^4.0.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
+  integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+cross-spawn@^7.0.0, cross-spawn@^7.0.2:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+csstype@^3.0.2:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+  integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+
+damerau-levenshtein@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
+  integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
+
+data-view-buffer@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2"
+  integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==
+  dependencies:
+    call-bind "^1.0.6"
+    es-errors "^1.3.0"
+    is-data-view "^1.0.1"
+
+data-view-byte-length@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2"
+  integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==
+  dependencies:
+    call-bind "^1.0.7"
+    es-errors "^1.3.0"
+    is-data-view "^1.0.1"
+
+data-view-byte-offset@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a"
+  integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==
+  dependencies:
+    call-bind "^1.0.6"
+    es-errors "^1.3.0"
+    is-data-view "^1.0.1"
+
+debug@^3.2.7:
+  version "3.2.7"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
+  integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
+  dependencies:
+    ms "^2.1.1"
+
+debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5:
+  version "4.3.7"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+  integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
+  dependencies:
+    ms "^2.1.3"
+
+deep-is@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+  integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+define-data-property@^1.0.1, define-data-property@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
+  integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
+  dependencies:
+    es-define-property "^1.0.0"
+    es-errors "^1.3.0"
+    gopd "^1.0.1"
+
+define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
+  integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
+  dependencies:
+    define-data-property "^1.0.1"
+    has-property-descriptors "^1.0.0"
+    object-keys "^1.1.1"
+
+detect-libc@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
+  integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
+
+didyoumean@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
+  integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
+
+dlv@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
+  integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
+
+doctrine@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+  integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
+  dependencies:
+    esutils "^2.0.2"
+
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+  dependencies:
+    esutils "^2.0.2"
+
+eastasianwidth@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+  integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
+
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emoji-regex@^9.2.2:
+  version "9.2.2"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+  integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+
+enhanced-resolve@^5.15.0:
+  version "5.17.1"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15"
+  integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==
+  dependencies:
+    graceful-fs "^4.2.4"
+    tapable "^2.2.0"
+
+es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3:
+  version "1.23.3"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0"
+  integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==
+  dependencies:
+    array-buffer-byte-length "^1.0.1"
+    arraybuffer.prototype.slice "^1.0.3"
+    available-typed-arrays "^1.0.7"
+    call-bind "^1.0.7"
+    data-view-buffer "^1.0.1"
+    data-view-byte-length "^1.0.1"
+    data-view-byte-offset "^1.0.0"
+    es-define-property "^1.0.0"
+    es-errors "^1.3.0"
+    es-object-atoms "^1.0.0"
+    es-set-tostringtag "^2.0.3"
+    es-to-primitive "^1.2.1"
+    function.prototype.name "^1.1.6"
+    get-intrinsic "^1.2.4"
+    get-symbol-description "^1.0.2"
+    globalthis "^1.0.3"
+    gopd "^1.0.1"
+    has-property-descriptors "^1.0.2"
+    has-proto "^1.0.3"
+    has-symbols "^1.0.3"
+    hasown "^2.0.2"
+    internal-slot "^1.0.7"
+    is-array-buffer "^3.0.4"
+    is-callable "^1.2.7"
+    is-data-view "^1.0.1"
+    is-negative-zero "^2.0.3"
+    is-regex "^1.1.4"
+    is-shared-array-buffer "^1.0.3"
+    is-string "^1.0.7"
+    is-typed-array "^1.1.13"
+    is-weakref "^1.0.2"
+    object-inspect "^1.13.1"
+    object-keys "^1.1.1"
+    object.assign "^4.1.5"
+    regexp.prototype.flags "^1.5.2"
+    safe-array-concat "^1.1.2"
+    safe-regex-test "^1.0.3"
+    string.prototype.trim "^1.2.9"
+    string.prototype.trimend "^1.0.8"
+    string.prototype.trimstart "^1.0.8"
+    typed-array-buffer "^1.0.2"
+    typed-array-byte-length "^1.0.1"
+    typed-array-byte-offset "^1.0.2"
+    typed-array-length "^1.0.6"
+    unbox-primitive "^1.0.2"
+    which-typed-array "^1.1.15"
+
+es-define-property@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
+  integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
+  dependencies:
+    get-intrinsic "^1.2.4"
+
+es-errors@^1.2.1, es-errors@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+  integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
+es-iterator-helpers@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz#f6d745d342aea214fe09497e7152170dc333a7a6"
+  integrity sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.3"
+    es-errors "^1.3.0"
+    es-set-tostringtag "^2.0.3"
+    function-bind "^1.1.2"
+    get-intrinsic "^1.2.4"
+    globalthis "^1.0.4"
+    has-property-descriptors "^1.0.2"
+    has-proto "^1.0.3"
+    has-symbols "^1.0.3"
+    internal-slot "^1.0.7"
+    iterator.prototype "^1.1.3"
+    safe-array-concat "^1.1.2"
+
+es-object-atoms@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941"
+  integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==
+  dependencies:
+    es-errors "^1.3.0"
+
+es-set-tostringtag@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777"
+  integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==
+  dependencies:
+    get-intrinsic "^1.2.4"
+    has-tostringtag "^1.0.2"
+    hasown "^2.0.1"
+
+es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763"
+  integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==
+  dependencies:
+    hasown "^2.0.0"
+
+es-to-primitive@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+  integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
+  dependencies:
+    is-callable "^1.1.4"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.2"
+
+escape-string-regexp@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+  integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-config-next@15.0.1:
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.0.1.tgz#5f49a01d312420cdbf1e87299396ef779ae99004"
+  integrity sha512-3cYCrgbH6GS/ufApza7XCKz92vtq4dAdYhx++rMFNlH2cAV+/GsAKkrr4+bohYOACmzG2nAOR+uWprKC1Uld6A==
+  dependencies:
+    "@next/eslint-plugin-next" "15.0.1"
+    "@rushstack/eslint-patch" "^1.10.3"
+    "@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+    "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+    eslint-import-resolver-node "^0.3.6"
+    eslint-import-resolver-typescript "^3.5.2"
+    eslint-plugin-import "^2.31.0"
+    eslint-plugin-jsx-a11y "^6.10.0"
+    eslint-plugin-react "^7.35.0"
+    eslint-plugin-react-hooks "^5.0.0"
+
+eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9:
+  version "0.3.9"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac"
+  integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==
+  dependencies:
+    debug "^3.2.7"
+    is-core-module "^2.13.0"
+    resolve "^1.22.4"
+
+eslint-import-resolver-typescript@^3.5.2:
+  version "3.6.3"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz#bb8e388f6afc0f940ce5d2c5fd4a3d147f038d9e"
+  integrity sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==
+  dependencies:
+    "@nolyfill/is-core-module" "1.0.39"
+    debug "^4.3.5"
+    enhanced-resolve "^5.15.0"
+    eslint-module-utils "^2.8.1"
+    fast-glob "^3.3.2"
+    get-tsconfig "^4.7.5"
+    is-bun-module "^1.0.2"
+    is-glob "^4.0.3"
+
+eslint-module-utils@^2.12.0, eslint-module-utils@^2.8.1:
+  version "2.12.0"
+  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b"
+  integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==
+  dependencies:
+    debug "^3.2.7"
+
+eslint-plugin-import@^2.31.0:
+  version "2.31.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7"
+  integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==
+  dependencies:
+    "@rtsao/scc" "^1.1.0"
+    array-includes "^3.1.8"
+    array.prototype.findlastindex "^1.2.5"
+    array.prototype.flat "^1.3.2"
+    array.prototype.flatmap "^1.3.2"
+    debug "^3.2.7"
+    doctrine "^2.1.0"
+    eslint-import-resolver-node "^0.3.9"
+    eslint-module-utils "^2.12.0"
+    hasown "^2.0.2"
+    is-core-module "^2.15.1"
+    is-glob "^4.0.3"
+    minimatch "^3.1.2"
+    object.fromentries "^2.0.8"
+    object.groupby "^1.0.3"
+    object.values "^1.2.0"
+    semver "^6.3.1"
+    string.prototype.trimend "^1.0.8"
+    tsconfig-paths "^3.15.0"
+
+eslint-plugin-jsx-a11y@^6.10.0:
+  version "6.10.2"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483"
+  integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==
+  dependencies:
+    aria-query "^5.3.2"
+    array-includes "^3.1.8"
+    array.prototype.flatmap "^1.3.2"
+    ast-types-flow "^0.0.8"
+    axe-core "^4.10.0"
+    axobject-query "^4.1.0"
+    damerau-levenshtein "^1.0.8"
+    emoji-regex "^9.2.2"
+    hasown "^2.0.2"
+    jsx-ast-utils "^3.3.5"
+    language-tags "^1.0.9"
+    minimatch "^3.1.2"
+    object.fromentries "^2.0.8"
+    safe-regex-test "^1.0.3"
+    string.prototype.includes "^2.0.1"
+
+eslint-plugin-react-hooks@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz#72e2eefbac4b694f5324154619fee44f5f60f101"
+  integrity sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==
+
+eslint-plugin-react@^7.35.0:
+  version "7.37.2"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz#cd0935987876ba2900df2f58339f6d92305acc7a"
+  integrity sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==
+  dependencies:
+    array-includes "^3.1.8"
+    array.prototype.findlast "^1.2.5"
+    array.prototype.flatmap "^1.3.2"
+    array.prototype.tosorted "^1.1.4"
+    doctrine "^2.1.0"
+    es-iterator-helpers "^1.1.0"
+    estraverse "^5.3.0"
+    hasown "^2.0.2"
+    jsx-ast-utils "^2.4.1 || ^3.0.0"
+    minimatch "^3.1.2"
+    object.entries "^1.1.8"
+    object.fromentries "^2.0.8"
+    object.values "^1.2.0"
+    prop-types "^15.8.1"
+    resolve "^2.0.0-next.5"
+    semver "^6.3.1"
+    string.prototype.matchall "^4.0.11"
+    string.prototype.repeat "^1.0.0"
+
+eslint-scope@^7.2.2:
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
+  integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^5.2.0"
+
+eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
+  integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+
+eslint@^8:
+  version "8.57.1"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9"
+  integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
+    "@eslint-community/regexpp" "^4.6.1"
+    "@eslint/eslintrc" "^2.1.4"
+    "@eslint/js" "8.57.1"
+    "@humanwhocodes/config-array" "^0.13.0"
+    "@humanwhocodes/module-importer" "^1.0.1"
+    "@nodelib/fs.walk" "^1.2.8"
+    "@ungap/structured-clone" "^1.2.0"
+    ajv "^6.12.4"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.2"
+    debug "^4.3.2"
+    doctrine "^3.0.0"
+    escape-string-regexp "^4.0.0"
+    eslint-scope "^7.2.2"
+    eslint-visitor-keys "^3.4.3"
+    espree "^9.6.1"
+    esquery "^1.4.2"
+    esutils "^2.0.2"
+    fast-deep-equal "^3.1.3"
+    file-entry-cache "^6.0.1"
+    find-up "^5.0.0"
+    glob-parent "^6.0.2"
+    globals "^13.19.0"
+    graphemer "^1.4.0"
+    ignore "^5.2.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    is-path-inside "^3.0.3"
+    js-yaml "^4.1.0"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    levn "^0.4.1"
+    lodash.merge "^4.6.2"
+    minimatch "^3.1.2"
+    natural-compare "^1.4.0"
+    optionator "^0.9.3"
+    strip-ansi "^6.0.1"
+    text-table "^0.2.0"
+
+espree@^9.6.0, espree@^9.6.1:
+  version "9.6.1"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
+  integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
+  dependencies:
+    acorn "^8.9.0"
+    acorn-jsx "^5.3.2"
+    eslint-visitor-keys "^3.4.1"
+
+esquery@^1.4.2:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
+  integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
+  dependencies:
+    estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+  integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+  dependencies:
+    estraverse "^5.2.0"
+
+estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-glob@3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4"
+  integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==
+  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.4"
+
+fast-glob@^3.3.0, fast-glob@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
+  integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
+  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.4"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fastq@^1.6.0:
+  version "1.17.1"
+  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
+  integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==
+  dependencies:
+    reusify "^1.0.4"
+
+file-entry-cache@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+  integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+  dependencies:
+    flat-cache "^3.0.4"
+
+fill-range@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+  integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+find-up@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+  dependencies:
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
+
+flat-cache@^3.0.4:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
+  integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
+  dependencies:
+    flatted "^3.2.9"
+    keyv "^4.5.3"
+    rimraf "^3.0.2"
+
+flatted@^3.2.9:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
+  integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
+
+for-each@^0.3.3:
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
+  integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
+  dependencies:
+    is-callable "^1.1.3"
+
+foreground-child@^3.1.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77"
+  integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==
+  dependencies:
+    cross-spawn "^7.0.0"
+    signal-exit "^4.0.1"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+fsevents@~2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+function-bind@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+function.prototype.name@^1.1.6:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd"
+  integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+    functions-have-names "^1.2.3"
+
+functions-have-names@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
+  integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
+
+get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
+  integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
+  dependencies:
+    es-errors "^1.3.0"
+    function-bind "^1.1.2"
+    has-proto "^1.0.1"
+    has-symbols "^1.0.3"
+    hasown "^2.0.0"
+
+get-symbol-description@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5"
+  integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==
+  dependencies:
+    call-bind "^1.0.5"
+    es-errors "^1.3.0"
+    get-intrinsic "^1.2.4"
+
+get-tsconfig@^4.7.5:
+  version "4.8.1"
+  resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471"
+  integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==
+  dependencies:
+    resolve-pkg-maps "^1.0.0"
+
+glob-parent@^5.1.2, glob-parent@~5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+  integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+  dependencies:
+    is-glob "^4.0.1"
+
+glob-parent@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+  dependencies:
+    is-glob "^4.0.3"
+
+glob@^10.3.10:
+  version "10.4.5"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
+  integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
+  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"
+
+glob@^7.1.3:
+  version "7.2.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.1.1"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+globals@^13.19.0:
+  version "13.24.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
+  integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
+  dependencies:
+    type-fest "^0.20.2"
+
+globalthis@^1.0.3, globalthis@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236"
+  integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==
+  dependencies:
+    define-properties "^1.2.1"
+    gopd "^1.0.1"
+
+gopd@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+  integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+  dependencies:
+    get-intrinsic "^1.1.3"
+
+graceful-fs@^4.2.4:
+  version "4.2.11"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+  integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+graphemer@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
+  integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+
+has-bigints@^1.0.1, has-bigints@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
+  integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
+  integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
+  dependencies:
+    es-define-property "^1.0.0"
+
+has-proto@^1.0.1, has-proto@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
+  integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
+
+has-symbols@^1.0.2, has-symbols@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+  integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has-tostringtag@^1.0.0, has-tostringtag@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
+  integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
+  dependencies:
+    has-symbols "^1.0.3"
+
+hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+  integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+  dependencies:
+    function-bind "^1.1.2"
+
+ignore@^5.2.0, ignore@^5.3.1:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
+  integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+
+import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+internal-slot@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802"
+  integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==
+  dependencies:
+    es-errors "^1.3.0"
+    hasown "^2.0.0"
+    side-channel "^1.0.4"
+
+is-array-buffer@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98"
+  integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.2.1"
+
+is-arrayish@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+  integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
+is-async-function@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646"
+  integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
+is-bigint@^1.0.1:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
+  integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
+  dependencies:
+    has-bigints "^1.0.1"
+
+is-binary-path@~2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+  integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+  dependencies:
+    binary-extensions "^2.0.0"
+
+is-boolean-object@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
+  integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
+  dependencies:
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
+
+is-bun-module@^1.0.2:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-1.2.1.tgz#495e706f42e29f086fd5fe1ac3c51f106062b9fc"
+  integrity sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==
+  dependencies:
+    semver "^7.6.3"
+
+is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
+  integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
+
+is-core-module@^2.13.0, is-core-module@^2.15.1:
+  version "2.15.1"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37"
+  integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==
+  dependencies:
+    hasown "^2.0.2"
+
+is-data-view@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f"
+  integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==
+  dependencies:
+    is-typed-array "^1.1.13"
+
+is-date-object@^1.0.1, is-date-object@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
+  integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-finalizationregistry@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6"
+  integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==
+  dependencies:
+    call-bind "^1.0.2"
+
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-generator-function@^1.0.10:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
+  integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-map@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
+  integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
+
+is-negative-zero@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747"
+  integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==
+
+is-number-object@^1.0.4:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
+  integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-path-inside@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+  integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
+is-regex@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
+  integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
+  dependencies:
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
+
+is-set@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d"
+  integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==
+
+is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688"
+  integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==
+  dependencies:
+    call-bind "^1.0.7"
+
+is-string@^1.0.5, is-string@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
+  integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
+is-symbol@^1.0.2, is-symbol@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
+  integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
+  dependencies:
+    has-symbols "^1.0.2"
+
+is-typed-array@^1.1.13:
+  version "1.1.13"
+  resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229"
+  integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==
+  dependencies:
+    which-typed-array "^1.1.14"
+
+is-weakmap@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd"
+  integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==
+
+is-weakref@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
+  integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
+  dependencies:
+    call-bind "^1.0.2"
+
+is-weakset@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007"
+  integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==
+  dependencies:
+    call-bind "^1.0.7"
+    get-intrinsic "^1.2.4"
+
+isarray@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+  integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+iterator.prototype@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.3.tgz#016c2abe0be3bbdb8319852884f60908ac62bf9c"
+  integrity sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==
+  dependencies:
+    define-properties "^1.2.1"
+    get-intrinsic "^1.2.1"
+    has-symbols "^1.0.3"
+    reflect.getprototypeof "^1.0.4"
+    set-function-name "^2.0.1"
+
+jackspeak@^3.1.2:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
+  integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==
+  dependencies:
+    "@isaacs/cliui" "^8.0.2"
+  optionalDependencies:
+    "@pkgjs/parseargs" "^0.11.0"
+
+jiti@^1.21.0:
+  version "1.21.6"
+  resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268"
+  integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
+
+"js-tokens@^3.0.0 || ^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+  integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+  dependencies:
+    argparse "^2.0.1"
+
+json-buffer@3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+  integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+json5@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
+  integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
+  dependencies:
+    minimist "^1.2.0"
+
+"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5:
+  version "3.3.5"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
+  integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==
+  dependencies:
+    array-includes "^3.1.6"
+    array.prototype.flat "^1.3.1"
+    object.assign "^4.1.4"
+    object.values "^1.1.6"
+
+keyv@^4.5.3:
+  version "4.5.4"
+  resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+  integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+  dependencies:
+    json-buffer "3.0.1"
+
+language-subtag-registry@^0.3.20:
+  version "0.3.23"
+  resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7"
+  integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==
+
+language-tags@^1.0.9:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777"
+  integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==
+  dependencies:
+    language-subtag-registry "^0.3.20"
+
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
+lilconfig@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
+  integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==
+
+lilconfig@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb"
+  integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==
+
+lines-and-columns@^1.1.6:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+  integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+locate-path@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+  dependencies:
+    p-locate "^5.0.0"
+
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+loose-envify@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+lru-cache@^10.2.0:
+  version "10.4.3"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
+  integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
+
+merge2@^1.3.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+micromatch@^4.0.4, micromatch@^4.0.5:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+  integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
+  dependencies:
+    braces "^3.0.3"
+    picomatch "^2.3.1"
+
+minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimatch@^9.0.4:
+  version "9.0.5"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
+  integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+  dependencies:
+    brace-expansion "^2.0.1"
+
+minimist@^1.2.0, minimist@^1.2.6:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+  integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
+  integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
+
+ms@^2.1.1, ms@^2.1.3:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+mz@^2.7.0:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
+  integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
+  dependencies:
+    any-promise "^1.0.0"
+    object-assign "^4.0.1"
+    thenify-all "^1.0.0"
+
+nanoid@^3.3.6, nanoid@^3.3.7:
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
+  integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+next@15.0.1:
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/next/-/next-15.0.1.tgz#a0e8eda35d803cb7f8092b2a2eb9d072e22bf21d"
+  integrity sha512-PSkFkr/w7UnFWm+EP8y/QpHrJXMqpZzAXpergB/EqLPOh4SGPJXv1wj4mslr2hUZBAS9pX7/9YLIdxTv6fwytw==
+  dependencies:
+    "@next/env" "15.0.1"
+    "@swc/counter" "0.1.3"
+    "@swc/helpers" "0.5.13"
+    busboy "1.6.0"
+    caniuse-lite "^1.0.30001579"
+    postcss "8.4.31"
+    styled-jsx "5.1.6"
+  optionalDependencies:
+    "@next/swc-darwin-arm64" "15.0.1"
+    "@next/swc-darwin-x64" "15.0.1"
+    "@next/swc-linux-arm64-gnu" "15.0.1"
+    "@next/swc-linux-arm64-musl" "15.0.1"
+    "@next/swc-linux-x64-gnu" "15.0.1"
+    "@next/swc-linux-x64-musl" "15.0.1"
+    "@next/swc-win32-arm64-msvc" "15.0.1"
+    "@next/swc-win32-x64-msvc" "15.0.1"
+    sharp "^0.33.5"
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+object-assign@^4.0.1, object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
+
+object-hash@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
+  integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
+
+object-inspect@^1.13.1:
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
+  integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
+
+object-keys@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+  integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object.assign@^4.1.4, object.assign@^4.1.5:
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
+  integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
+  dependencies:
+    call-bind "^1.0.5"
+    define-properties "^1.2.1"
+    has-symbols "^1.0.3"
+    object-keys "^1.1.1"
+
+object.entries@^1.1.8:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41"
+  integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-object-atoms "^1.0.0"
+
+object.fromentries@^2.0.8:
+  version "2.0.8"
+  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65"
+  integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.2"
+    es-object-atoms "^1.0.0"
+
+object.groupby@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e"
+  integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.2"
+
+object.values@^1.1.6, object.values@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b"
+  integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-object-atoms "^1.0.0"
+
+once@^1.3.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+  dependencies:
+    wrappy "1"
+
+optionator@^0.9.3:
+  version "0.9.4"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
+  integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==
+  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"
+
+p-limit@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+  integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+  dependencies:
+    yocto-queue "^0.1.0"
+
+p-locate@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+  dependencies:
+    p-limit "^3.0.2"
+
+package-json-from-dist@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
+  integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+path-exists@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+  integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+  integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-scurry@^1.11.1:
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
+  integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
+  dependencies:
+    lru-cache "^10.2.0"
+    minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
+
+picocolors@^1.0.0, picocolors@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pify@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+  integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
+
+pirates@^4.0.1:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
+  integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
+
+possible-typed-array-names@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
+  integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==
+
+postcss-import@^15.1.0:
+  version "15.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70"
+  integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==
+  dependencies:
+    postcss-value-parser "^4.0.0"
+    read-cache "^1.0.0"
+    resolve "^1.1.7"
+
+postcss-js@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2"
+  integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==
+  dependencies:
+    camelcase-css "^2.0.1"
+
+postcss-load-config@^4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3"
+  integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==
+  dependencies:
+    lilconfig "^3.0.0"
+    yaml "^2.3.4"
+
+postcss-nested@^6.0.1:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131"
+  integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==
+  dependencies:
+    postcss-selector-parser "^6.1.1"
+
+postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.1.1:
+  version "6.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de"
+  integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
+  dependencies:
+    cssesc "^3.0.0"
+    util-deprecate "^1.0.2"
+
+postcss-value-parser@^4.0.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+postcss@8.4.31:
+  version "8.4.31"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
+  integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
+  dependencies:
+    nanoid "^3.3.6"
+    picocolors "^1.0.0"
+    source-map-js "^1.0.2"
+
+postcss@^8, postcss@^8.4.23:
+  version "8.4.47"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
+  integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
+  dependencies:
+    nanoid "^3.3.7"
+    picocolors "^1.1.0"
+    source-map-js "^1.2.1"
+
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prop-types@^15.8.1:
+  version "15.8.1"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+  integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+  dependencies:
+    loose-envify "^1.4.0"
+    object-assign "^4.1.1"
+    react-is "^16.13.1"
+
+punycode@^2.1.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+  integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+queue-microtask@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+react-dom@19.0.0-rc-69d4b800-20241021:
+  version "19.0.0-rc-69d4b800-20241021"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0-rc-69d4b800-20241021.tgz#e27b4f2c962236e9ece496a0ea1c9c7161608ea0"
+  integrity sha512-ZXBsP/kTDLI9QopUaUgYJhmmAhO8aKz7DCv2Ui2rA9boCfJ/dRRh6BlVidsyb2dPzG01rItdRFQqwbP+x9s5Rg==
+  dependencies:
+    scheduler "0.25.0-rc-69d4b800-20241021"
+
+react-is@^16.13.1:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react@19.0.0-rc-69d4b800-20241021:
+  version "19.0.0-rc-69d4b800-20241021"
+  resolved "https://registry.yarnpkg.com/react/-/react-19.0.0-rc-69d4b800-20241021.tgz#2c3ce2a581df8c4c9e4059189af29b89022edd41"
+  integrity sha512-dXki4tN+rP+4xhsm65q/QI/19VCZdu5vPcy4h6zaJt20XP8/1r/LCwrLFYuj8hElbNz5AmxW6JtRa7ej0BzZdg==
+
+read-cache@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
+  integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==
+  dependencies:
+    pify "^2.3.0"
+
+readdirp@~3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+  integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+  dependencies:
+    picomatch "^2.2.1"
+
+reflect-metadata@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b"
+  integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==
+
+reflect.getprototypeof@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859"
+  integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.1"
+    es-errors "^1.3.0"
+    get-intrinsic "^1.2.4"
+    globalthis "^1.0.3"
+    which-builtin-type "^1.1.3"
+
+regexp.prototype.flags@^1.5.2:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42"
+  integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-errors "^1.3.0"
+    set-function-name "^2.0.2"
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-pkg-maps@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f"
+  integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
+
+resolve@^1.1.7, resolve@^1.22.2, resolve@^1.22.4:
+  version "1.22.8"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
+  integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
+  dependencies:
+    is-core-module "^2.13.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+resolve@^2.0.0-next.5:
+  version "2.0.0-next.5"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c"
+  integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==
+  dependencies:
+    is-core-module "^2.13.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+reusify@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+  dependencies:
+    glob "^7.1.3"
+
+run-parallel@^1.1.9:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+  dependencies:
+    queue-microtask "^1.2.2"
+
+safe-array-concat@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb"
+  integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==
+  dependencies:
+    call-bind "^1.0.7"
+    get-intrinsic "^1.2.4"
+    has-symbols "^1.0.3"
+    isarray "^2.0.5"
+
+safe-regex-test@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377"
+  integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==
+  dependencies:
+    call-bind "^1.0.6"
+    es-errors "^1.3.0"
+    is-regex "^1.1.4"
+
+scheduler@0.25.0-rc-69d4b800-20241021:
+  version "0.25.0-rc-69d4b800-20241021"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-69d4b800-20241021.tgz#336e47ef2bd5eddb0ebacfd910b5df1b236d92bd"
+  integrity sha512-S5AYX/YhMAN6u9AXgKYbZP4U4ZklC6R9Q7HmFSBk7d4DLiHVNxvAvlSvuM4nxFkwOk50MnpfTKQ7UWHXDOc9Eg==
+
+semver@^6.3.1:
+  version "6.3.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+  integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
+semver@^7.6.0, semver@^7.6.3:
+  version "7.6.3"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
+  integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
+
+set-function-length@^1.2.1:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
+  integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
+  dependencies:
+    define-data-property "^1.1.4"
+    es-errors "^1.3.0"
+    function-bind "^1.1.2"
+    get-intrinsic "^1.2.4"
+    gopd "^1.0.1"
+    has-property-descriptors "^1.0.2"
+
+set-function-name@^2.0.1, set-function-name@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985"
+  integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==
+  dependencies:
+    define-data-property "^1.1.4"
+    es-errors "^1.3.0"
+    functions-have-names "^1.2.3"
+    has-property-descriptors "^1.0.2"
+
+sharp@^0.33.5:
+  version "0.33.5"
+  resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e"
+  integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==
+  dependencies:
+    color "^4.2.3"
+    detect-libc "^2.0.3"
+    semver "^7.6.3"
+  optionalDependencies:
+    "@img/sharp-darwin-arm64" "0.33.5"
+    "@img/sharp-darwin-x64" "0.33.5"
+    "@img/sharp-libvips-darwin-arm64" "1.0.4"
+    "@img/sharp-libvips-darwin-x64" "1.0.4"
+    "@img/sharp-libvips-linux-arm" "1.0.5"
+    "@img/sharp-libvips-linux-arm64" "1.0.4"
+    "@img/sharp-libvips-linux-s390x" "1.0.4"
+    "@img/sharp-libvips-linux-x64" "1.0.4"
+    "@img/sharp-libvips-linuxmusl-arm64" "1.0.4"
+    "@img/sharp-libvips-linuxmusl-x64" "1.0.4"
+    "@img/sharp-linux-arm" "0.33.5"
+    "@img/sharp-linux-arm64" "0.33.5"
+    "@img/sharp-linux-s390x" "0.33.5"
+    "@img/sharp-linux-x64" "0.33.5"
+    "@img/sharp-linuxmusl-arm64" "0.33.5"
+    "@img/sharp-linuxmusl-x64" "0.33.5"
+    "@img/sharp-wasm32" "0.33.5"
+    "@img/sharp-win32-ia32" "0.33.5"
+    "@img/sharp-win32-x64" "0.33.5"
+
+shebang-command@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+  dependencies:
+    shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+  integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+side-channel@^1.0.4, side-channel@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
+  integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
+  dependencies:
+    call-bind "^1.0.7"
+    es-errors "^1.3.0"
+    get-intrinsic "^1.2.4"
+    object-inspect "^1.13.1"
+
+signal-exit@^4.0.1:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+  integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
+simple-swizzle@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+  integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==
+  dependencies:
+    is-arrayish "^0.3.1"
+
+source-map-js@^1.0.2, source-map-js@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+  integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
+streamsearch@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
+  integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
+
+"string-width-cjs@npm:string-width@^4.2.0":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string-width@^4.1.0:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string-width@^5.0.1, string-width@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+  integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+  dependencies:
+    eastasianwidth "^0.2.0"
+    emoji-regex "^9.2.2"
+    strip-ansi "^7.0.1"
+
+string.prototype.includes@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92"
+  integrity sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.3"
+
+string.prototype.matchall@^4.0.11:
+  version "4.0.11"
+  resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a"
+  integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.2"
+    es-errors "^1.3.0"
+    es-object-atoms "^1.0.0"
+    get-intrinsic "^1.2.4"
+    gopd "^1.0.1"
+    has-symbols "^1.0.3"
+    internal-slot "^1.0.7"
+    regexp.prototype.flags "^1.5.2"
+    set-function-name "^2.0.2"
+    side-channel "^1.0.6"
+
+string.prototype.repeat@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a"
+  integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.5"
+
+string.prototype.trim@^1.2.9:
+  version "1.2.9"
+  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4"
+  integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-abstract "^1.23.0"
+    es-object-atoms "^1.0.0"
+
+string.prototype.trimend@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229"
+  integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-object-atoms "^1.0.0"
+
+string.prototype.trimstart@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde"
+  integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+    es-object-atoms "^1.0.0"
+
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-ansi@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+  integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+  dependencies:
+    ansi-regex "^6.0.1"
+
+strip-bom@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+  integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
+
+strip-json-comments@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+styled-jsx@5.1.6:
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499"
+  integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==
+  dependencies:
+    client-only "0.0.1"
+
+sucrase@^3.32.0:
+  version "3.35.0"
+  resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263"
+  integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.2"
+    commander "^4.0.0"
+    glob "^10.3.10"
+    lines-and-columns "^1.1.6"
+    mz "^2.7.0"
+    pirates "^4.0.1"
+    ts-interface-checker "^0.1.9"
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+tailwindcss@^3.4.1:
+  version "3.4.14"
+  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.14.tgz#6dd23a7f54ec197b19159e91e3bb1e55e7aa73ac"
+  integrity sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==
+  dependencies:
+    "@alloc/quick-lru" "^5.2.0"
+    arg "^5.0.2"
+    chokidar "^3.5.3"
+    didyoumean "^1.2.2"
+    dlv "^1.1.3"
+    fast-glob "^3.3.0"
+    glob-parent "^6.0.2"
+    is-glob "^4.0.3"
+    jiti "^1.21.0"
+    lilconfig "^2.1.0"
+    micromatch "^4.0.5"
+    normalize-path "^3.0.0"
+    object-hash "^3.0.0"
+    picocolors "^1.0.0"
+    postcss "^8.4.23"
+    postcss-import "^15.1.0"
+    postcss-js "^4.0.1"
+    postcss-load-config "^4.0.1"
+    postcss-nested "^6.0.1"
+    postcss-selector-parser "^6.0.11"
+    resolve "^1.22.2"
+    sucrase "^3.32.0"
+
+tapable@^2.2.0:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
+  integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
+
+text-table@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+  integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
+thenify-all@^1.0.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
+  integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==
+  dependencies:
+    thenify ">= 3.1.0 < 4"
+
+"thenify@>= 3.1.0 < 4":
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
+  integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
+  dependencies:
+    any-promise "^1.0.0"
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+ts-api-utils@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
+  integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
+
+ts-interface-checker@^0.1.9:
+  version "0.1.13"
+  resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
+  integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
+
+tsconfig-paths@^3.15.0:
+  version "3.15.0"
+  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
+  integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==
+  dependencies:
+    "@types/json5" "^0.0.29"
+    json5 "^1.0.2"
+    minimist "^1.2.6"
+    strip-bom "^3.0.0"
+
+tslib@^1.9.3:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
+tslib@^2.4.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b"
+  integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==
+
+tsyringe@^4.8.0:
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.8.0.tgz#d599651b36793ba872870fee4f845bd484a5cac1"
+  integrity sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==
+  dependencies:
+    tslib "^1.9.3"
+
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+typed-array-buffer@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3"
+  integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==
+  dependencies:
+    call-bind "^1.0.7"
+    es-errors "^1.3.0"
+    is-typed-array "^1.1.13"
+
+typed-array-byte-length@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67"
+  integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==
+  dependencies:
+    call-bind "^1.0.7"
+    for-each "^0.3.3"
+    gopd "^1.0.1"
+    has-proto "^1.0.3"
+    is-typed-array "^1.1.13"
+
+typed-array-byte-offset@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063"
+  integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==
+  dependencies:
+    available-typed-arrays "^1.0.7"
+    call-bind "^1.0.7"
+    for-each "^0.3.3"
+    gopd "^1.0.1"
+    has-proto "^1.0.3"
+    is-typed-array "^1.1.13"
+
+typed-array-length@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3"
+  integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==
+  dependencies:
+    call-bind "^1.0.7"
+    for-each "^0.3.3"
+    gopd "^1.0.1"
+    has-proto "^1.0.3"
+    is-typed-array "^1.1.13"
+    possible-typed-array-names "^1.0.0"
+
+typescript@^5:
+  version "5.6.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
+  integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
+
+unbox-primitive@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
+  integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
+  dependencies:
+    call-bind "^1.0.2"
+    has-bigints "^1.0.2"
+    has-symbols "^1.0.3"
+    which-boxed-primitive "^1.0.2"
+
+undici-types@~6.19.2:
+  version "6.19.8"
+  resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
+  integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
+
+uri-js@^4.2.2:
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+  integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+  dependencies:
+    punycode "^2.1.0"
+
+util-deprecate@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+which-boxed-primitive@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
+  integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
+  dependencies:
+    is-bigint "^1.0.1"
+    is-boolean-object "^1.1.0"
+    is-number-object "^1.0.4"
+    is-string "^1.0.5"
+    is-symbol "^1.0.3"
+
+which-builtin-type@^1.1.3:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.4.tgz#592796260602fc3514a1b5ee7fa29319b72380c3"
+  integrity sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==
+  dependencies:
+    function.prototype.name "^1.1.6"
+    has-tostringtag "^1.0.2"
+    is-async-function "^2.0.0"
+    is-date-object "^1.0.5"
+    is-finalizationregistry "^1.0.2"
+    is-generator-function "^1.0.10"
+    is-regex "^1.1.4"
+    is-weakref "^1.0.2"
+    isarray "^2.0.5"
+    which-boxed-primitive "^1.0.2"
+    which-collection "^1.0.2"
+    which-typed-array "^1.1.15"
+
+which-collection@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0"
+  integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==
+  dependencies:
+    is-map "^2.0.3"
+    is-set "^2.0.3"
+    is-weakmap "^2.0.2"
+    is-weakset "^2.0.3"
+
+which-typed-array@^1.1.14, which-typed-array@^1.1.15:
+  version "1.1.15"
+  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d"
+  integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==
+  dependencies:
+    available-typed-arrays "^1.0.7"
+    call-bind "^1.0.7"
+    for-each "^0.3.3"
+    gopd "^1.0.1"
+    has-tostringtag "^1.0.2"
+
+which@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+  integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+  dependencies:
+    isexe "^2.0.0"
+
+word-wrap@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
+  integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
+
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
+wrap-ansi@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
+  integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
+  dependencies:
+    ansi-styles "^6.1.0"
+    string-width "^5.0.1"
+    strip-ansi "^7.0.1"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+yaml@^2.3.4:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3"
+  integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==
+
+yocto-queue@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+  integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
-- 
2.39.5


From f6fba115e322797959033d08e808cf2e99e8c70a Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Thu, 31 Oct 2024 18:28:01 +0300
Subject: [PATCH 02/37] Add base architecture

---
 components.json                               | 20 ++++++
 package.json                                  |  6 ++
 src/{ => app}/components/button/button-vm.ts  |  0
 src/{ => app}/components/button/button.tsx    |  6 +-
 src/app/globals.css                           | 71 ++++++++++++++++---
 src/app/layout.tsx                            |  1 -
 src/app/test/client/view/parent-view.tsx      | 13 ++++
 src/app/test/client/vm/test-button-vm.ts      | 34 +++++++++
 src/app/test/layout.tsx                       | 10 +++
 src/app/test/modules/test-app-module.ts       | 13 ++++
 src/app/test/page.tsx                         |  7 +-
 src/app/test/vm/test-button-vm.ts             | 17 -----
 src/bootstrap/di/di-context.tsx               | 21 ++++++
 src/bootstrap/di/init-di.ts                   |  4 +-
 src/bootstrap/helpers/view/base-view.tsx      | 32 ++++-----
 src/bootstrap/helpers/vm/base-vm.ts           |  5 ++
 src/bootstrap/helpers/vm/vm-decorator.ts      |  9 ---
 src/feature/common/server-di.ts               | 13 ++++
 .../service/test-get-button-title-service.ts  |  9 +++
 .../domain/test/service/test-service-repo.ts  |  5 ++
 src/feature/domain/test/test-module-key.ts    |  1 +
 src/feature/infra/test/module/test-module.ts  | 12 ++++
 src/feature/infra/test/repo/test-repo-iml.ts  | 14 ++++
 src/lib/utils.ts                              |  6 ++
 tailwind.config.ts                            | 61 +++++++++++++---
 yarn.lock                                     | 37 ++++++++++
 26 files changed, 355 insertions(+), 72 deletions(-)
 create mode 100644 components.json
 rename src/{ => app}/components/button/button-vm.ts (100%)
 rename src/{ => app}/components/button/button.tsx (80%)
 create mode 100644 src/app/test/client/view/parent-view.tsx
 create mode 100644 src/app/test/client/vm/test-button-vm.ts
 create mode 100644 src/app/test/layout.tsx
 create mode 100644 src/app/test/modules/test-app-module.ts
 delete mode 100644 src/app/test/vm/test-button-vm.ts
 create mode 100644 src/bootstrap/di/di-context.tsx
 delete mode 100644 src/bootstrap/helpers/vm/vm-decorator.ts
 create mode 100644 src/feature/common/server-di.ts
 create mode 100644 src/feature/domain/test/service/test-get-button-title-service.ts
 create mode 100644 src/feature/domain/test/service/test-service-repo.ts
 create mode 100644 src/feature/domain/test/test-module-key.ts
 create mode 100644 src/feature/infra/test/module/test-module.ts
 create mode 100644 src/feature/infra/test/repo/test-repo-iml.ts
 create mode 100644 src/lib/utils.ts

diff --git a/components.json b/components.json
new file mode 100644
index 0000000..7a63543
--- /dev/null
+++ b/components.json
@@ -0,0 +1,20 @@
+{
+  "$schema": "https://ui.shadcn.com/schema.json",
+  "style": "new-york",
+  "rsc": true,
+  "tsx": true,
+  "tailwind": {
+    "config": "tailwind.config.ts",
+    "css": "src/app/globals.css",
+    "baseColor": "zinc",
+    "cssVariables": true,
+    "prefix": ""
+  },
+  "aliases": {
+    "components": "@/components",
+    "utils": "@/lib/utils",
+    "ui": "@/components/ui",
+    "lib": "@/lib",
+    "hooks": "@/hooks"
+  }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 2db330d..95b1c2b 100644
--- a/package.json
+++ b/package.json
@@ -9,10 +9,16 @@
     "lint": "next lint"
   },
   "dependencies": {
+    "@radix-ui/react-icons": "^1.3.1",
+    "class-variance-authority": "^0.7.0",
+    "clsx": "^2.1.1",
+    "lucide-react": "^0.454.0",
     "next": "15.0.1",
     "react": "19.0.0-rc-69d4b800-20241021",
     "react-dom": "19.0.0-rc-69d4b800-20241021",
     "reflect-metadata": "^0.2.2",
+    "tailwind-merge": "^2.5.4",
+    "tailwindcss-animate": "^1.0.7",
     "tsyringe": "^4.8.0"
   },
   "devDependencies": {
diff --git a/src/components/button/button-vm.ts b/src/app/components/button/button-vm.ts
similarity index 100%
rename from src/components/button/button-vm.ts
rename to src/app/components/button/button-vm.ts
diff --git a/src/components/button/button.tsx b/src/app/components/button/button.tsx
similarity index 80%
rename from src/components/button/button.tsx
rename to src/app/components/button/button.tsx
index 6b0452b..e1b8a4d 100644
--- a/src/components/button/button.tsx
+++ b/src/app/components/button/button.tsx
@@ -1,10 +1,12 @@
+"use client"
 import BaseView, { BuildProps } from "@/bootstrap/helpers/view/base-view";
-import ButtonVm from "@/components/button/button-vm";
+import ButtonVm from "@/app/components/button/button-vm";
 import { ReactNode } from "react";
 
 export default class Button extends BaseView<ButtonVm> {
     protected Build(props: BuildProps<ButtonVm>): ReactNode {
         const {vm} = props
+        
         return <button onClick={vm.onClick} >{vm.props.title}</button>
     }
-}
\ No newline at end of file
+}
diff --git a/src/app/globals.css b/src/app/globals.css
index 6b717ad..942f871 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -2,20 +2,71 @@
 @tailwind components;
 @tailwind utilities;
 
-:root {
-  --background: #ffffff;
-  --foreground: #171717;
+body {
+  font-family: Arial, Helvetica, sans-serif;
 }
 
-@media (prefers-color-scheme: dark) {
+@layer base {
   :root {
-    --background: #0a0a0a;
-    --foreground: #ededed;
+    --background: 0 0% 100%;
+    --foreground: 240 10% 3.9%;
+    --card: 0 0% 100%;
+    --card-foreground: 240 10% 3.9%;
+    --popover: 0 0% 100%;
+    --popover-foreground: 240 10% 3.9%;
+    --primary: 240 5.9% 10%;
+    --primary-foreground: 0 0% 98%;
+    --secondary: 240 4.8% 95.9%;
+    --secondary-foreground: 240 5.9% 10%;
+    --muted: 240 4.8% 95.9%;
+    --muted-foreground: 240 3.8% 46.1%;
+    --accent: 240 4.8% 95.9%;
+    --accent-foreground: 240 5.9% 10%;
+    --destructive: 0 84.2% 60.2%;
+    --destructive-foreground: 0 0% 98%;
+    --border: 240 5.9% 90%;
+    --input: 240 5.9% 90%;
+    --ring: 240 10% 3.9%;
+    --chart-1: 12 76% 61%;
+    --chart-2: 173 58% 39%;
+    --chart-3: 197 37% 24%;
+    --chart-4: 43 74% 66%;
+    --chart-5: 27 87% 67%;
+    --radius: 0.5rem;
+  }
+  .dark {
+    --background: 240 10% 3.9%;
+    --foreground: 0 0% 98%;
+    --card: 240 10% 3.9%;
+    --card-foreground: 0 0% 98%;
+    --popover: 240 10% 3.9%;
+    --popover-foreground: 0 0% 98%;
+    --primary: 0 0% 98%;
+    --primary-foreground: 240 5.9% 10%;
+    --secondary: 240 3.7% 15.9%;
+    --secondary-foreground: 0 0% 98%;
+    --muted: 240 3.7% 15.9%;
+    --muted-foreground: 240 5% 64.9%;
+    --accent: 240 3.7% 15.9%;
+    --accent-foreground: 0 0% 98%;
+    --destructive: 0 62.8% 30.6%;
+    --destructive-foreground: 0 0% 98%;
+    --border: 240 3.7% 15.9%;
+    --input: 240 3.7% 15.9%;
+    --ring: 240 4.9% 83.9%;
+    --chart-1: 220 70% 50%;
+    --chart-2: 160 60% 45%;
+    --chart-3: 30 80% 55%;
+    --chart-4: 280 65% 60%;
+    --chart-5: 340 75% 55%;
   }
 }
 
-body {
-  color: var(--foreground);
-  background: var(--background);
-  font-family: Arial, Helvetica, sans-serif;
+@layer base {
+  * {
+    @apply border-border;
+  }
+  body {
+    @apply bg-background text-foreground;
+  }
 }
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 1cd9211..ba8b4a7 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,4 +1,3 @@
-import "reflect-metadata"
 import type { Metadata } from "next";
 import localFont from "next/font/local";
 import "./globals.css";
diff --git a/src/app/test/client/view/parent-view.tsx b/src/app/test/client/view/parent-view.tsx
new file mode 100644
index 0000000..81de26a
--- /dev/null
+++ b/src/app/test/client/view/parent-view.tsx
@@ -0,0 +1,13 @@
+"use client"
+import Button from "@/app/components/button/button"
+import { TestButtonVM } from "@/app/test/client/vm/test-button-vm"
+import { useDI } from "@/bootstrap/di/di-context"
+import { useRef } from "react"
+
+export default function ParentView() {
+   const di = useDI()
+   
+    const vmRef = useRef(di.resolve(TestButtonVM))
+
+    return <Button vm={vmRef.current} />
+}
\ No newline at end of file
diff --git a/src/app/test/client/vm/test-button-vm.ts b/src/app/test/client/vm/test-button-vm.ts
new file mode 100644
index 0000000..1a74c04
--- /dev/null
+++ b/src/app/test/client/vm/test-button-vm.ts
@@ -0,0 +1,34 @@
+"use client"
+import BaseVM from "@/bootstrap/helpers/vm/base-vm";
+import ButtonVm from "@/app/components/button/button-vm";
+import { useEffect, useState } from "react";
+import getButtonTitle from "@/feature/domain/test/service/test-get-button-title-service";
+
+export class TestButtonVM extends BaseVM<ButtonVm> {
+    private getButtonTitle: () => Promise<string>
+
+    constructor() {
+        super()
+        this.getButtonTitle = this.di.resolve(getButtonTitle.name)
+    }
+
+    useVM(): ButtonVm {
+        const [ buttonTitle, setTitle ] = useState("Default title")
+        useEffect(() => {
+            (async () => {
+                const title = await this.getButtonTitle()
+                setTitle(title)
+            })()
+        }, [])
+        return {
+            props: {
+                title: buttonTitle
+            },
+            onClick: () => {
+                console.log("clicked on the button");
+            }
+        }
+    }
+}
+
+export const testKey = "testKey"
\ No newline at end of file
diff --git a/src/app/test/layout.tsx b/src/app/test/layout.tsx
new file mode 100644
index 0000000..173e5ca
--- /dev/null
+++ b/src/app/test/layout.tsx
@@ -0,0 +1,10 @@
+"use client"
+import testAppModule from "@/app/test/modules/test-app-module";
+import { DiContext } from "@/bootstrap/di/di-context";
+import { PropsWithChildren, useRef } from "react";
+
+export default function WithDILayout(props: PropsWithChildren) {
+    const testDi = useRef(testAppModule())
+    return <DiContext.Provider value={testDi.current}>{props.children}</DiContext.Provider>
+}
+
diff --git a/src/app/test/modules/test-app-module.ts b/src/app/test/modules/test-app-module.ts
new file mode 100644
index 0000000..7f4d702
--- /dev/null
+++ b/src/app/test/modules/test-app-module.ts
@@ -0,0 +1,13 @@
+import { TestButtonVM, testKey } from "@/app/test/client/vm/test-button-vm";
+import di from "@/bootstrap/di/init-di"
+import getButtonTitle from "@/feature/domain/test/service/test-get-button-title-service";
+
+export default function testAppModule() {
+    const testDi = di.createChildContainer()
+    
+    testDi.registerInstance(testKey, TestButtonVM);
+    testDi.register(getButtonTitle.name, {
+        useValue: getButtonTitle
+    })
+    return testDi
+}
diff --git a/src/app/test/page.tsx b/src/app/test/page.tsx
index f08fc5e..fb067fd 100644
--- a/src/app/test/page.tsx
+++ b/src/app/test/page.tsx
@@ -1,8 +1,5 @@
-"use client"
-import "reflect-metadata"
-import TestButtonVM from "@/app/test/vm/test-button-vm";
-import Button from "@/components/button/button";
+import ParentView from "@/app/test/client/view/parent-view";
 
 export default function Page() {
-    return <Button vmName={TestButtonVM.name} />
+    return <ParentView />
 }
\ No newline at end of file
diff --git a/src/app/test/vm/test-button-vm.ts b/src/app/test/vm/test-button-vm.ts
deleted file mode 100644
index 414d5ce..0000000
--- a/src/app/test/vm/test-button-vm.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import BaseVM from "@/bootstrap/helpers/vm/base-vm";
-import injectableVm from "@/bootstrap/helpers/vm/vm-decorator";
-import ButtonVm from "@/components/button/button-vm";
-
-@injectableVm()
-export default class TestButtonVM extends BaseVM<ButtonVm> {
-    useVM(): ButtonVm {
-        return {
-            props: {
-                title: "Test Button"
-            },
-            onClick: () => {
-                console.log("clicked on the button");
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/bootstrap/di/di-context.tsx b/src/bootstrap/di/di-context.tsx
new file mode 100644
index 0000000..2474156
--- /dev/null
+++ b/src/bootstrap/di/di-context.tsx
@@ -0,0 +1,21 @@
+"use client"
+import di from "@/bootstrap/di/init-di";
+import { createContext, use } from "react";
+import { DependencyContainer } from "tsyringe";
+
+const DiContext = createContext<null | DependencyContainer>(di)
+
+const useDI = () => {
+    const di = use(DiContext)
+
+    if (!di) {
+        throw new Error("Di has not provided")
+    }
+
+    return di
+}
+
+export {
+    DiContext,
+    useDI,
+}
\ No newline at end of file
diff --git a/src/bootstrap/di/init-di.ts b/src/bootstrap/di/init-di.ts
index 79281ec..3ebdeef 100644
--- a/src/bootstrap/di/init-di.ts
+++ b/src/bootstrap/di/init-di.ts
@@ -1,5 +1,5 @@
+// "use client"
 import "reflect-metadata"
-
 import { container, DependencyContainer } from "tsyringe";
 
 /**
@@ -8,7 +8,7 @@ import { container, DependencyContainer } from "tsyringe";
  *  are registered and available for injection throughout the application.
  */
 const InitDI = (): DependencyContainer => {
-  const di = container;
+  const di = container.createChildContainer();
 
   return di;
 };
diff --git a/src/bootstrap/helpers/view/base-view.tsx b/src/bootstrap/helpers/view/base-view.tsx
index a7498cf..a1eecc7 100644
--- a/src/bootstrap/helpers/view/base-view.tsx
+++ b/src/bootstrap/helpers/view/base-view.tsx
@@ -1,17 +1,17 @@
 "use client"
+// import gdi from "@/bootstrap/di/init-di";
 /* eslint-disable react/display-name */
 /* eslint-disable @typescript-eslint/no-explicit-any */
 /* eslint-disable react/jsx-props-no-spreading */
-import di from "@/bootstrap/di/init-di";
-import BaseVM from "@/bootstrap/helpers/vm/base-vm";
-import { Component, ReactNode, FC, PropsWithChildren, memo, MemoExoticComponent } from "react";
+import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
+import { Component, ReactNode, FC, PropsWithChildren, memo } from "react";
 
 /* -------------------------------------------------------------------------- */
 /*                             Connector Component                            */
 /* -------------------------------------------------------------------------- */
 interface IVvmConnector<IVM, PROPS> extends PropsWithChildren {
   View: FC<any & { vm: IVM }>;
-  vmName: string;
+  Vm: IBaseVM<IVM>;
   restProps?: PROPS;
   memoizedByVM?: boolean;
 }
@@ -21,11 +21,9 @@ interface IVvmConnector<IVM, PROPS> extends PropsWithChildren {
  */
 const VvmConnector = memo(
   <IVM, PROPS>(props: IVvmConnector<IVM, PROPS>) => {
-    const { View, vmName, restProps, children } = props;
-    const VmInstance = di.resolve(vmName) as new () => BaseVM<IVM>;
-    if (!VmInstance) throw new Error(`Provided vm as ${vmName} is not exists`)
+    const { View, Vm, restProps, children } = props;
 
-    const vm = new VmInstance().useVM()
+    const vm = Vm.useVM()
 
     const allProps = {
       restProps,
@@ -46,8 +44,8 @@ const VvmConnector = memo(
 type IVMParent = Record<string, any>;
 type IPropParent = Record<string, any> | undefined;
 
-type BaseProps<PROPS extends IPropParent = undefined> = {
-  vmName: string;
+type BaseProps<IVM extends IVMParent, PROPS extends IPropParent = undefined> = {
+  vm: IBaseVM<IVM>;
   restProps?: PROPS;
   /**
    * By default it's true.
@@ -70,25 +68,23 @@ export type BuildProps<
 export default abstract class BaseView<
   IVM extends IVMParent,
   PROPS extends IPropParent = undefined,
-> extends Component<BaseProps<PROPS>> {
+> extends Component<BaseProps<IVM, PROPS>> {
   /* -------------------------------- Abstracts ------------------------------- */
   protected abstract Build(props: BuildProps<IVM, PROPS>): ReactNode;
 
   /* -------------------------------- Renderer -------------------------------- */
   render(): ReactNode {
-    const { vmName, restProps, memoizedByVM, children, ...rest } = this.props;
+    const { vm, restProps, memoizedByVM, children, ...rest } = this.props;
     
-    const Connector = VvmConnector as MemoExoticComponent<((props: IVvmConnector<IVM, PROPS>) => JSX.Element)>;
-
     return (
-      <Connector
+      <VvmConnector
         View={this.Build}
-        vmName={vmName}
+        Vm={vm}
         memoizedByVM={typeof memoizedByVM === "undefined" ? true : memoizedByVM}
-        restProps={{ ...restProps, ...rest } as PROPS}
+        restProps={{ ...restProps, ...rest }}
       >
         {children}
-      </Connector>
+      </VvmConnector>
     );
   }
   /* -------------------------------------------------------------------------- */
diff --git a/src/bootstrap/helpers/vm/base-vm.ts b/src/bootstrap/helpers/vm/base-vm.ts
index e82a80e..b9bb379 100644
--- a/src/bootstrap/helpers/vm/base-vm.ts
+++ b/src/bootstrap/helpers/vm/base-vm.ts
@@ -1,4 +1,5 @@
 "use client"
+import { useDI } from "@/bootstrap/di/di-context";
 import { NoOverride } from "@/bootstrap/helpers/type-helper";
 import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
 import { useState } from "react";
@@ -28,6 +29,10 @@ export default abstract class BaseVM<
    */
   protected rerender?: () => void;
 
+  /* -------------------------------------------------------------------------- */
+  protected get di() {
+    return useDI()
+  }
   /* -------------------------------------------------------------------------- */
   /**
    * You can use this hook in your useVm method to get rerender method
diff --git a/src/bootstrap/helpers/vm/vm-decorator.ts b/src/bootstrap/helpers/vm/vm-decorator.ts
deleted file mode 100644
index 2092738..0000000
--- a/src/bootstrap/helpers/vm/vm-decorator.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-"use client"
-import di from "@/bootstrap/di/init-di";
-import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
-
-export default function injectableVm() {
-    return function (target: new (...args: unknown[]) => IBaseVM<object>) {
-        di.registerInstance(target.name, target);
-    };
-}
\ No newline at end of file
diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
new file mode 100644
index 0000000..54200bd
--- /dev/null
+++ b/src/feature/common/server-di.ts
@@ -0,0 +1,13 @@
+import { testModuleKey } from "@/feature/domain/test/test-module-key";
+import getTestModule from "@/feature/infra/test/module/test-module";
+import { DependencyContainer } from "tsyringe";
+
+export default function serverDi(module: string): DependencyContainer {
+    const getDi = {
+        [testModuleKey]: getTestModule
+    }[module]
+
+    if (!getDi) throw new Error("Server Di didn't found for module: " + module)
+
+    return getDi()
+}
\ No newline at end of file
diff --git a/src/feature/domain/test/service/test-get-button-title-service.ts b/src/feature/domain/test/service/test-get-button-title-service.ts
new file mode 100644
index 0000000..c9a4910
--- /dev/null
+++ b/src/feature/domain/test/service/test-get-button-title-service.ts
@@ -0,0 +1,9 @@
+"use server"
+import serverDi from "@/feature/common/server-di";
+import TestRepo, { testRepoKey } from "@/feature/domain/test/service/test-service-repo"
+import { testModuleKey } from "@/feature/domain/test/test-module-key";
+
+export default async function getButtonTitle() {
+    const repo = serverDi(testModuleKey).resolve<TestRepo>(testRepoKey) 
+    return repo.getButtonTitle()
+}
diff --git a/src/feature/domain/test/service/test-service-repo.ts b/src/feature/domain/test/service/test-service-repo.ts
new file mode 100644
index 0000000..91e00f8
--- /dev/null
+++ b/src/feature/domain/test/service/test-service-repo.ts
@@ -0,0 +1,5 @@
+export default interface TestRepo {
+    getButtonTitle(): Promise<string>
+}
+
+export const testRepoKey = "restRepoKey"
\ No newline at end of file
diff --git a/src/feature/domain/test/test-module-key.ts b/src/feature/domain/test/test-module-key.ts
new file mode 100644
index 0000000..abb15e6
--- /dev/null
+++ b/src/feature/domain/test/test-module-key.ts
@@ -0,0 +1 @@
+export const testModuleKey = "testModuleKey"
\ No newline at end of file
diff --git a/src/feature/infra/test/module/test-module.ts b/src/feature/infra/test/module/test-module.ts
new file mode 100644
index 0000000..d8963b8
--- /dev/null
+++ b/src/feature/infra/test/module/test-module.ts
@@ -0,0 +1,12 @@
+import di from "@/bootstrap/di/init-di"
+import { testRepoKey } from "@/feature/domain/test/service/test-service-repo"
+import TestRepoImpl from "@/feature/infra/test/repo/test-repo-iml"
+
+export default function getTestModule() {
+    const testDi = di.createChildContainer()
+
+    di.register(testRepoKey, {
+        useClass: TestRepoImpl
+    })
+    return testDi
+}
\ No newline at end of file
diff --git a/src/feature/infra/test/repo/test-repo-iml.ts b/src/feature/infra/test/repo/test-repo-iml.ts
new file mode 100644
index 0000000..f519bee
--- /dev/null
+++ b/src/feature/infra/test/repo/test-repo-iml.ts
@@ -0,0 +1,14 @@
+import TestRepo from "@/feature/domain/test/service/test-service-repo";
+
+export default class TestRepoImpl implements TestRepo {
+    async getButtonTitle(): Promise<string> {
+        await new Promise((res) => {
+            setTimeout(() => {
+                res(true)
+            }, 3000)
+        })
+        console.log('hereee');
+        return Promise.resolve("Button title")
+    }
+
+}
\ No newline at end of file
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..bd0c391
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+  return twMerge(clsx(inputs))
+}
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 021c393..1f7474c 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -1,19 +1,64 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
 import type { Config } from "tailwindcss";
 
 const config: Config = {
-  content: [
+    darkMode: ["class"],
+    content: [
     "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
     "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
     "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
   ],
   theme: {
-    extend: {
-      colors: {
-        background: "var(--background)",
-        foreground: "var(--foreground)",
-      },
-    },
+  	extend: {
+  		colors: {
+  			background: 'hsl(var(--background))',
+  			foreground: 'hsl(var(--foreground))',
+  			card: {
+  				DEFAULT: 'hsl(var(--card))',
+  				foreground: 'hsl(var(--card-foreground))'
+  			},
+  			popover: {
+  				DEFAULT: 'hsl(var(--popover))',
+  				foreground: 'hsl(var(--popover-foreground))'
+  			},
+  			primary: {
+  				DEFAULT: 'hsl(var(--primary))',
+  				foreground: 'hsl(var(--primary-foreground))'
+  			},
+  			secondary: {
+  				DEFAULT: 'hsl(var(--secondary))',
+  				foreground: 'hsl(var(--secondary-foreground))'
+  			},
+  			muted: {
+  				DEFAULT: 'hsl(var(--muted))',
+  				foreground: 'hsl(var(--muted-foreground))'
+  			},
+  			accent: {
+  				DEFAULT: 'hsl(var(--accent))',
+  				foreground: 'hsl(var(--accent-foreground))'
+  			},
+  			destructive: {
+  				DEFAULT: 'hsl(var(--destructive))',
+  				foreground: 'hsl(var(--destructive-foreground))'
+  			},
+  			border: 'hsl(var(--border))',
+  			input: 'hsl(var(--input))',
+  			ring: 'hsl(var(--ring))',
+  			chart: {
+  				'1': 'hsl(var(--chart-1))',
+  				'2': 'hsl(var(--chart-2))',
+  				'3': 'hsl(var(--chart-3))',
+  				'4': 'hsl(var(--chart-4))',
+  				'5': 'hsl(var(--chart-5))'
+  			}
+  		},
+  		borderRadius: {
+  			lg: 'var(--radius)',
+  			md: 'calc(var(--radius) - 2px)',
+  			sm: 'calc(var(--radius) - 4px)'
+  		}
+  	}
   },
-  plugins: [],
+  plugins: [require("tailwindcss-animate")],
 };
 export default config;
diff --git a/yarn.lock b/yarn.lock
index 05b8294..c6c3ff2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -305,6 +305,11 @@
   resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
   integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
 
+"@radix-ui/react-icons@^1.3.1":
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.1.tgz#462c85fd726a77854cd5956e29eb19a575aa7ce5"
+  integrity sha512-QvYompk0X+8Yjlo/Fv4McrzxohDdM5GgLHyQcPpcsPvlOSXCGFjdbuyGL5dzRbg0GpknAjQJJZzdiRK7iWVuFQ==
+
 "@rtsao/scc@^1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
@@ -714,11 +719,28 @@ chokidar@^3.5.3:
   optionalDependencies:
     fsevents "~2.3.2"
 
+class-variance-authority@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522"
+  integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==
+  dependencies:
+    clsx "2.0.0"
+
 client-only@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
   integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
 
+clsx@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
+  integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
+
+clsx@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
+  integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
+
 color-convert@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
@@ -1871,6 +1893,11 @@ lru-cache@^10.2.0:
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
   integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
 
+lucide-react@^0.454.0:
+  version "0.454.0"
+  resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.454.0.tgz#a81b9c482018720f07ead0503ae502d94d528444"
+  integrity sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==
+
 merge2@^1.3.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@@ -2599,6 +2626,16 @@ supports-preserve-symlinks-flag@^1.0.0:
   resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
   integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
 
+tailwind-merge@^2.5.4:
+  version "2.5.4"
+  resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.5.4.tgz#4bf574e81fa061adeceba099ae4df56edcee78d1"
+  integrity sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==
+
+tailwindcss-animate@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4"
+  integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==
+
 tailwindcss@^3.4.1:
   version "3.4.14"
   resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.14.tgz#6dd23a7f54ec197b19159e91e3bb1e55e7aa73ac"
-- 
2.39.5


From 80e0d8ec5437d8caee53ac875cdcb8bc03863fd2 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Thu, 31 Oct 2024 19:09:19 +0300
Subject: [PATCH 03/37] Add base ui of dashboard

---
 public/file.svg                            |   2 +-
 public/globe.svg                           |   2 +-
 public/window.svg                          |   2 +-
 src/app/components/icons/document.tsx      |   5 +
 src/app/components/icons/home.tsx          |   5 +
 src/app/components/icons/user.tsx          |   7 +
 src/app/components/skeleton/skeletons.tsx  | 218 +++++++++++++++++++++
 src/app/dashboard/(overview)/loading.tsx   |   5 +
 src/app/dashboard/(overview)/page.tsx      |  22 +++
 src/app/dashboard/components/nav-links.tsx |  45 +++++
 src/app/dashboard/components/sidenav.tsx   |  21 ++
 src/app/dashboard/layout.tsx               |  13 ++
 src/app/page.tsx                           | 106 ++--------
 13 files changed, 355 insertions(+), 98 deletions(-)
 create mode 100644 src/app/components/icons/document.tsx
 create mode 100644 src/app/components/icons/home.tsx
 create mode 100644 src/app/components/icons/user.tsx
 create mode 100644 src/app/components/skeleton/skeletons.tsx
 create mode 100644 src/app/dashboard/(overview)/loading.tsx
 create mode 100644 src/app/dashboard/(overview)/page.tsx
 create mode 100644 src/app/dashboard/components/nav-links.tsx
 create mode 100644 src/app/dashboard/components/sidenav.tsx
 create mode 100644 src/app/dashboard/layout.tsx

diff --git a/public/file.svg b/public/file.svg
index 004145c..adfd2a9 100644
--- a/public/file.svg
+++ b/public/file.svg
@@ -1 +1 @@
-<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
\ No newline at end of file
+<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clipRule="evenodd" fill="#666" fillRule="evenodd"/></svg>
\ No newline at end of file
diff --git a/public/globe.svg b/public/globe.svg
index 567f17b..4af126a 100644
--- a/public/globe.svg
+++ b/public/globe.svg
@@ -1 +1 @@
-<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
\ No newline at end of file
+<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fillRule="evenodd" clipRule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
\ No newline at end of file
diff --git a/public/window.svg b/public/window.svg
index b2b2a44..98febf2 100644
--- a/public/window.svg
+++ b/public/window.svg
@@ -1 +1 @@
-<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
\ No newline at end of file
+<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fillRule="evenodd" clipRule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
\ No newline at end of file
diff --git a/src/app/components/icons/document.tsx b/src/app/components/icons/document.tsx
new file mode 100644
index 0000000..a8cbcb6
--- /dev/null
+++ b/src/app/components/icons/document.tsx
@@ -0,0 +1,5 @@
+export function DocumentIcon(props: {className?: string}) {
+    return (
+        <svg className={props.className} width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 2.5C3 2.22386 3.22386 2 3.5 2H9.08579C9.21839 2 9.34557 2.05268 9.43934 2.14645L11.8536 4.56066C11.9473 4.65443 12 4.78161 12 4.91421V12.5C12 12.7761 11.7761 13 11.5 13H3.5C3.22386 13 3 12.7761 3 12.5V2.5ZM3.5 1C2.67157 1 2 1.67157 2 2.5V12.5C2 13.3284 2.67157 14 3.5 14H11.5C12.3284 14 13 13.3284 13 12.5V4.91421C13 4.51639 12.842 4.13486 12.5607 3.85355L10.1464 1.43934C9.86514 1.15804 9.48361 1 9.08579 1H3.5ZM4.5 4C4.22386 4 4 4.22386 4 4.5C4 4.77614 4.22386 5 4.5 5H7.5C7.77614 5 8 4.77614 8 4.5C8 4.22386 7.77614 4 7.5 4H4.5ZM4.5 7C4.22386 7 4 7.22386 4 7.5C4 7.77614 4.22386 8 4.5 8H10.5C10.7761 8 11 7.77614 11 7.5C11 7.22386 10.7761 7 10.5 7H4.5ZM4.5 10C4.22386 10 4 10.2239 4 10.5C4 10.7761 4.22386 11 4.5 11H10.5C10.7761 11 11 10.7761 11 10.5C11 10.2239 10.7761 10 10.5 10H4.5Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg>
+    )
+}
\ No newline at end of file
diff --git a/src/app/components/icons/home.tsx b/src/app/components/icons/home.tsx
new file mode 100644
index 0000000..8270d4e
--- /dev/null
+++ b/src/app/components/icons/home.tsx
@@ -0,0 +1,5 @@
+export default function HomeIcon(props: {className?: string}) {
+    return (
+        <svg className={props.className} width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.07926 0.222253C7.31275 -0.007434 7.6873 -0.007434 7.92079 0.222253L14.6708 6.86227C14.907 7.09465 14.9101 7.47453 14.6778 7.71076C14.4454 7.947 14.0655 7.95012 13.8293 7.71773L13 6.90201V12.5C13 12.7761 12.7762 13 12.5 13H2.50002C2.22388 13 2.00002 12.7761 2.00002 12.5V6.90201L1.17079 7.71773C0.934558 7.95012 0.554672 7.947 0.32229 7.71076C0.0899079 7.47453 0.0930283 7.09465 0.32926 6.86227L7.07926 0.222253ZM7.50002 1.49163L12 5.91831V12H10V8.49999C10 8.22385 9.77617 7.99999 9.50002 7.99999H6.50002C6.22388 7.99999 6.00002 8.22385 6.00002 8.49999V12H3.00002V5.91831L7.50002 1.49163ZM7.00002 12H9.00002V8.99999H7.00002V12Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg>
+    )
+}
\ No newline at end of file
diff --git a/src/app/components/icons/user.tsx b/src/app/components/icons/user.tsx
new file mode 100644
index 0000000..02b56ed
--- /dev/null
+++ b/src/app/components/icons/user.tsx
@@ -0,0 +1,7 @@
+export function UserIcon(props: {className?: string}) {
+    return (
+        <svg className={props.className} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
+            <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
+        </svg>
+    )
+}
\ No newline at end of file
diff --git a/src/app/components/skeleton/skeletons.tsx b/src/app/components/skeleton/skeletons.tsx
new file mode 100644
index 0000000..52b8a87
--- /dev/null
+++ b/src/app/components/skeleton/skeletons.tsx
@@ -0,0 +1,218 @@
+// Loading animation
+const shimmer =
+  'before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/60 before:to-transparent';
+
+export function CardSkeleton() {
+  return (
+    <div
+      className={`${shimmer} relative overflow-hidden rounded-xl bg-gray-100 p-2 shadow-sm`}
+    >
+      <div className="flex p-4">
+        <div className="h-5 w-5 rounded-md bg-gray-200" />
+        <div className="ml-2 h-6 w-16 rounded-md bg-gray-200 text-sm font-medium" />
+      </div>
+      <div className="flex items-center justify-center truncate rounded-xl bg-white px-4 py-8">
+        <div className="h-7 w-20 rounded-md bg-gray-200" />
+      </div>
+    </div>
+  );
+}
+
+export function CardsSkeleton() {
+  return (
+    <>
+      <CardSkeleton />
+      <CardSkeleton />
+      <CardSkeleton />
+      <CardSkeleton />
+    </>
+  );
+}
+
+export function RevenueChartSkeleton() {
+  return (
+    <div className={`${shimmer} relative w-full overflow-hidden md:col-span-4`}>
+      <div className="mb-4 h-8 w-36 rounded-md bg-gray-100" />
+      <div className="rounded-xl bg-gray-100 p-4">
+        <div className="mt-0 grid h-[410px] grid-cols-12 items-end gap-2 rounded-md bg-white p-4 sm:grid-cols-13 md:gap-4" />
+        <div className="flex items-center pb-2 pt-6">
+          <div className="h-5 w-5 rounded-full bg-gray-200" />
+          <div className="ml-2 h-4 w-20 rounded-md bg-gray-200" />
+        </div>
+      </div>
+    </div>
+  );
+}
+
+export function InvoiceSkeleton() {
+  return (
+    <div className="flex flex-row items-center justify-between border-b border-gray-100 py-4">
+      <div className="flex items-center">
+        <div className="mr-2 h-8 w-8 rounded-full bg-gray-200" />
+        <div className="min-w-0">
+          <div className="h-5 w-40 rounded-md bg-gray-200" />
+          <div className="mt-2 h-4 w-12 rounded-md bg-gray-200" />
+        </div>
+      </div>
+      <div className="mt-2 h-4 w-12 rounded-md bg-gray-200" />
+    </div>
+  );
+}
+
+export function LatestInvoicesSkeleton() {
+  return (
+    <div
+      className={`${shimmer} relative flex w-full flex-col overflow-hidden md:col-span-4`}
+    >
+      <div className="mb-4 h-8 w-36 rounded-md bg-gray-100" />
+      <div className="flex grow flex-col justify-between rounded-xl bg-gray-100 p-4">
+        <div className="bg-white px-6">
+          <InvoiceSkeleton />
+          <InvoiceSkeleton />
+          <InvoiceSkeleton />
+          <InvoiceSkeleton />
+          <InvoiceSkeleton />
+          <div className="flex items-center pb-2 pt-6">
+            <div className="h-5 w-5 rounded-full bg-gray-200" />
+            <div className="ml-2 h-4 w-20 rounded-md bg-gray-200" />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+export default function DashboardSkeleton() {
+  return (
+    <>
+      <div
+        className={`${shimmer} relative mb-4 h-8 w-36 overflow-hidden rounded-md bg-gray-100`}
+      />
+      <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
+        <CardSkeleton />
+        <CardSkeleton />
+        <CardSkeleton />
+        <CardSkeleton />
+      </div>
+      <div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
+        <RevenueChartSkeleton />
+        <LatestInvoicesSkeleton />
+      </div>
+    </>
+  );
+}
+
+export function TableRowSkeleton() {
+  return (
+    <tr className="w-full border-b border-gray-100 last-of-type:border-none [&:first-child>td:first-child]:rounded-tl-lg [&:first-child>td:last-child]:rounded-tr-lg [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg">
+      {/* Customer Name and Image */}
+      <td className="relative overflow-hidden whitespace-nowrap py-3 pl-6 pr-3">
+        <div className="flex items-center gap-3">
+          <div className="h-8 w-8 rounded-full bg-gray-100"></div>
+          <div className="h-6 w-24 rounded bg-gray-100"></div>
+        </div>
+      </td>
+      {/* Email */}
+      <td className="whitespace-nowrap px-3 py-3">
+        <div className="h-6 w-32 rounded bg-gray-100"></div>
+      </td>
+      {/* Amount */}
+      <td className="whitespace-nowrap px-3 py-3">
+        <div className="h-6 w-16 rounded bg-gray-100"></div>
+      </td>
+      {/* Date */}
+      <td className="whitespace-nowrap px-3 py-3">
+        <div className="h-6 w-16 rounded bg-gray-100"></div>
+      </td>
+      {/* Status */}
+      <td className="whitespace-nowrap px-3 py-3">
+        <div className="h-6 w-16 rounded bg-gray-100"></div>
+      </td>
+      {/* Actions */}
+      <td className="whitespace-nowrap py-3 pl-6 pr-3">
+        <div className="flex justify-end gap-3">
+          <div className="h-[38px] w-[38px] rounded bg-gray-100"></div>
+          <div className="h-[38px] w-[38px] rounded bg-gray-100"></div>
+        </div>
+      </td>
+    </tr>
+  );
+}
+
+export function InvoicesMobileSkeleton() {
+  return (
+    <div className="mb-2 w-full rounded-md bg-white p-4">
+      <div className="flex items-center justify-between border-b border-gray-100 pb-8">
+        <div className="flex items-center">
+          <div className="mr-2 h-8 w-8 rounded-full bg-gray-100"></div>
+          <div className="h-6 w-16 rounded bg-gray-100"></div>
+        </div>
+        <div className="h-6 w-16 rounded bg-gray-100"></div>
+      </div>
+      <div className="flex w-full items-center justify-between pt-4">
+        <div>
+          <div className="h-6 w-16 rounded bg-gray-100"></div>
+          <div className="mt-2 h-6 w-24 rounded bg-gray-100"></div>
+        </div>
+        <div className="flex justify-end gap-2">
+          <div className="h-10 w-10 rounded bg-gray-100"></div>
+          <div className="h-10 w-10 rounded bg-gray-100"></div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+export function InvoicesTableSkeleton() {
+  return (
+    <div className="mt-6 flow-root">
+      <div className="inline-block min-w-full align-middle">
+        <div className="rounded-lg bg-gray-50 p-2 md:pt-0">
+          <div className="md:hidden">
+            <InvoicesMobileSkeleton />
+            <InvoicesMobileSkeleton />
+            <InvoicesMobileSkeleton />
+            <InvoicesMobileSkeleton />
+            <InvoicesMobileSkeleton />
+            <InvoicesMobileSkeleton />
+          </div>
+          <table className="hidden min-w-full text-gray-900 md:table">
+            <thead className="rounded-lg text-left text-sm font-normal">
+              <tr>
+                <th scope="col" className="px-4 py-5 font-medium sm:pl-6">
+                  Customer
+                </th>
+                <th scope="col" className="px-3 py-5 font-medium">
+                  Email
+                </th>
+                <th scope="col" className="px-3 py-5 font-medium">
+                  Amount
+                </th>
+                <th scope="col" className="px-3 py-5 font-medium">
+                  Date
+                </th>
+                <th scope="col" className="px-3 py-5 font-medium">
+                  Status
+                </th>
+                <th
+                  scope="col"
+                  className="relative pb-4 pl-3 pr-6 pt-2 sm:pr-6"
+                >
+                  <span className="sr-only">Edit</span>
+                </th>
+              </tr>
+            </thead>
+            <tbody className="bg-white">
+              <TableRowSkeleton />
+              <TableRowSkeleton />
+              <TableRowSkeleton />
+              <TableRowSkeleton />
+              <TableRowSkeleton />
+              <TableRowSkeleton />
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+  );
+}
diff --git a/src/app/dashboard/(overview)/loading.tsx b/src/app/dashboard/(overview)/loading.tsx
new file mode 100644
index 0000000..fcf5df1
--- /dev/null
+++ b/src/app/dashboard/(overview)/loading.tsx
@@ -0,0 +1,5 @@
+import DashboardSkeleton from "@/app/components/skeleton/skeletons";
+
+export default function Loading() {
+  return <DashboardSkeleton />;
+}
\ No newline at end of file
diff --git a/src/app/dashboard/(overview)/page.tsx b/src/app/dashboard/(overview)/page.tsx
new file mode 100644
index 0000000..60a12e0
--- /dev/null
+++ b/src/app/dashboard/(overview)/page.tsx
@@ -0,0 +1,22 @@
+
+export default async function Dashboard() {
+
+  return (
+    <main>
+      <h1 className={`mb-4 text-xl md:text-2xl`}>
+        Dashboard
+      </h1>
+      <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
+        {/* <CardWrapper /> */}
+      </div>
+      <div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
+        {/* <Suspense fallback={<RevenueChartSkeleton />}>
+          <RevenueChart  />
+        </Suspense> */}
+        {/* <Suspense fallback={<LatestInvoicesSkeleton />}>
+          <LatestInvoices />
+        </Suspense> */}
+      </div>
+    </main>
+  )
+}
diff --git a/src/app/dashboard/components/nav-links.tsx b/src/app/dashboard/components/nav-links.tsx
new file mode 100644
index 0000000..ecfca9e
--- /dev/null
+++ b/src/app/dashboard/components/nav-links.tsx
@@ -0,0 +1,45 @@
+'use client'
+import { DocumentIcon } from '@/app/components/icons/document';
+import HomeIcon from '@/app/components/icons/home';
+import { UserIcon } from '@/app/components/icons/user';
+import clsx from 'clsx';
+import Link from 'next/link'
+import { usePathname } from 'next/navigation';
+
+// Map of links to display in the side navigation.
+// Depending on the size of the application, this would be stored in a database.
+const links = [
+  { name: 'Home', href: '/dashboard', icon: HomeIcon },
+  {
+    name: 'Invoices',
+    href: '/dashboard/invoices',
+    icon: DocumentIcon,
+  },
+  { name: 'Customers', href: '/dashboard/customers', icon: UserIcon },
+];
+
+export default function NavLinks() {
+  const pathname = usePathname()
+  return (
+    <>
+      {links.map((link) => {
+        const LinkIcon = link.icon;
+        return (
+          <Link
+            key={link.name}
+            href={link.href}
+            className={clsx(
+              'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3',
+              {
+                'bg-sky-100 text-blue-600': pathname === link.href,
+              },
+            )}
+          >
+            <LinkIcon className="w-6" />
+            <p className="hidden md:block">{link.name}</p>
+          </Link>
+        );
+      })}
+    </>
+  );
+}
diff --git a/src/app/dashboard/components/sidenav.tsx b/src/app/dashboard/components/sidenav.tsx
new file mode 100644
index 0000000..7cf3d44
--- /dev/null
+++ b/src/app/dashboard/components/sidenav.tsx
@@ -0,0 +1,21 @@
+import NavLinks from '@/app/dashboard/components/nav-links';
+import Link from 'next/link';
+
+export default function SideNav() {
+  return (
+    <div className="flex h-full flex-col px-3 py-4 md:px-2">
+      <Link
+        className="mb-2 flex h-20 items-end justify-start rounded-md bg-blue-600 p-4 md:h-40"
+        href="/"
+      >
+        <div className="w-32 text-white md:w-40">
+          Home
+        </div>
+      </Link>
+      <div className="flex grow flex-row justify-between space-x-2 md:flex-col md:space-x-0 md:space-y-2">
+        <NavLinks />
+        <div className="hidden h-auto w-full grow rounded-md bg-gray-50 md:block"></div>
+      </div>
+    </div>
+  );
+}
diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx
new file mode 100644
index 0000000..d28eae9
--- /dev/null
+++ b/src/app/dashboard/layout.tsx
@@ -0,0 +1,13 @@
+import SideNav from "@/app/dashboard/components/sidenav";
+
+ 
+export default function Layout({ children }: { children: React.ReactNode }) {
+  return (
+    <div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
+      <div className="w-full flex-none md:w-64">
+        <SideNav />
+      </div>
+      <div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
+    </div>
+  );
+}
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 3eee014..b34fd95 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,101 +1,17 @@
-import Image from "next/image";
 
 export default function Home() {
   return (
-    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
-      <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
-        <Image
-          className="dark:invert"
-          src="/next.svg"
-          alt="Next.js logo"
-          width={180}
-          height={38}
-          priority
-        />
-        <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
-          <li className="mb-2">
-            Get started by editing{" "}
-            <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
-              src/app/page.tsx
-            </code>
-            .
-          </li>
-          <li>Save and see your changes instantly.</li>
-        </ol>
-
-        <div className="flex gap-4 items-center flex-col sm:flex-row">
-          <a
-            className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
-            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-            target="_blank"
-            rel="noopener noreferrer"
-          >
-            <Image
-              className="dark:invert"
-              src="/vercel.svg"
-              alt="Vercel logomark"
-              width={20}
-              height={20}
-            />
-            Deploy now
-          </a>
-          <a
-            className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
-            href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-            target="_blank"
-            rel="noopener noreferrer"
-          >
-            Read our docs
-          </a>
+    <main className="flex min-h-screen flex-col p-6">
+      <div className="mt-4 flex grow flex-col gap-4 md:flex-row">
+        <div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
+        <div />
+          <p className={`text-xl text-gray-800 md:text-3xl md:leading-normal`}>
+            <strong>Welcome to Acme.</strong> This is the example for the{' '}
+           
+            , brought to you by Vercel.
+          </p>
         </div>
-      </main>
-      <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
-        <a
-          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
-          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          <Image
-            aria-hidden
-            src="/file.svg"
-            alt="File icon"
-            width={16}
-            height={16}
-          />
-          Learn
-        </a>
-        <a
-          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
-          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          <Image
-            aria-hidden
-            src="/window.svg"
-            alt="Window icon"
-            width={16}
-            height={16}
-          />
-          Examples
-        </a>
-        <a
-          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
-          href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          <Image
-            aria-hidden
-            src="/globe.svg"
-            alt="Globe icon"
-            width={16}
-            height={16}
-          />
-          Go to nextjs.org →
-        </a>
-      </footer>
-    </div>
+      </div>
+    </main>
   );
 }
-- 
2.39.5


From fa26fa6ad62ad839ca1efc88d45de0b29884ec41 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Thu, 31 Oct 2024 19:55:06 +0300
Subject: [PATCH 04/37] Add seed file

---
 package.json                         |   6 +-
 src/bootstrap/db/placeholder-data.js | 188 ++++++++++++++++++
 src/bootstrap/db/seed.js             | 188 ++++++++++++++++++
 tsconfig.json                        |   2 +-
 yarn.lock                            | 273 +++++++++++++++++++++++++--
 5 files changed, 642 insertions(+), 15 deletions(-)
 create mode 100644 src/bootstrap/db/placeholder-data.js
 create mode 100644 src/bootstrap/db/seed.js

diff --git a/package.json b/package.json
index 95b1c2b..b9fb46b 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,8 @@
     "dev": "next dev --turbopack",
     "build": "next build",
     "start": "next start --port 4000",
-    "lint": "next lint"
+    "lint": "next lint",
+    "seed": "node -r dotenv/config ./src/bootstrap/db/seed.js"
   },
   "dependencies": {
     "@radix-ui/react-icons": "^1.3.1",
@@ -14,6 +15,7 @@
     "clsx": "^2.1.1",
     "lucide-react": "^0.454.0",
     "next": "15.0.1",
+    "postgres": "^3.4.5",
     "react": "19.0.0-rc-69d4b800-20241021",
     "react-dom": "19.0.0-rc-69d4b800-20241021",
     "reflect-metadata": "^0.2.2",
@@ -25,6 +27,8 @@
     "@types/node": "^20",
     "@types/react": "^18",
     "@types/react-dom": "^18",
+    "bcrypt": "^5.1.1",
+    "dotenv": "^16.4.5",
     "eslint": "^8",
     "eslint-config-next": "15.0.1",
     "postcss": "^8",
diff --git a/src/bootstrap/db/placeholder-data.js b/src/bootstrap/db/placeholder-data.js
new file mode 100644
index 0000000..15a4156
--- /dev/null
+++ b/src/bootstrap/db/placeholder-data.js
@@ -0,0 +1,188 @@
+// This file contains placeholder data that you'll be replacing with real data in the Data Fetching chapter:
+// https://nextjs.org/learn/dashboard-app/fetching-data
+const users = [
+  {
+    id: '410544b2-4001-4271-9855-fec4b6a6442a',
+    name: 'User',
+    email: 'user@nextmail.com',
+    password: '123456',
+  },
+];
+
+const customers = [
+  {
+    id: '3958dc9e-712f-4377-85e9-fec4b6a6442a',
+    name: 'Delba de Oliveira',
+    email: 'delba@oliveira.com',
+    image_url: '/customers/delba-de-oliveira.png',
+  },
+  {
+    id: '3958dc9e-742f-4377-85e9-fec4b6a6442a',
+    name: 'Lee Robinson',
+    email: 'lee@robinson.com',
+    image_url: '/customers/lee-robinson.png',
+  },
+  {
+    id: '3958dc9e-737f-4377-85e9-fec4b6a6442a',
+    name: 'Hector Simpson',
+    email: 'hector@simpson.com',
+    image_url: '/customers/hector-simpson.png',
+  },
+  {
+    id: '50ca3e18-62cd-11ee-8c99-0242ac120002',
+    name: 'Steven Tey',
+    email: 'steven@tey.com',
+    image_url: '/customers/steven-tey.png',
+  },
+  {
+    id: '3958dc9e-787f-4377-85e9-fec4b6a6442a',
+    name: 'Steph Dietz',
+    email: 'steph@dietz.com',
+    image_url: '/customers/steph-dietz.png',
+  },
+  {
+    id: '76d65c26-f784-44a2-ac19-586678f7c2f2',
+    name: 'Michael Novotny',
+    email: 'michael@novotny.com',
+    image_url: '/customers/michael-novotny.png',
+  },
+  {
+    id: 'd6e15727-9fe1-4961-8c5b-ea44a9bd81aa',
+    name: 'Evil Rabbit',
+    email: 'evil@rabbit.com',
+    image_url: '/customers/evil-rabbit.png',
+  },
+  {
+    id: '126eed9c-c90c-4ef6-a4a8-fcf7408d3c66',
+    name: 'Emil Kowalski',
+    email: 'emil@kowalski.com',
+    image_url: '/customers/emil-kowalski.png',
+  },
+  {
+    id: 'CC27C14A-0ACF-4F4A-A6C9-D45682C144B9',
+    name: 'Amy Burns',
+    email: 'amy@burns.com',
+    image_url: '/customers/amy-burns.png',
+  },
+  {
+    id: '13D07535-C59E-4157-A011-F8D2EF4E0CBB',
+    name: 'Balazs Orban',
+    email: 'balazs@orban.com',
+    image_url: '/customers/balazs-orban.png',
+  },
+];
+
+const invoices = [
+  {
+    customer_id: customers[0].id,
+    amount: 15795,
+    status: 'pending',
+    date: '2022-12-06',
+  },
+  {
+    customer_id: customers[1].id,
+    amount: 20348,
+    status: 'pending',
+    date: '2022-11-14',
+  },
+  {
+    customer_id: customers[4].id,
+    amount: 3040,
+    status: 'paid',
+    date: '2022-10-29',
+  },
+  {
+    customer_id: customers[3].id,
+    amount: 44800,
+    status: 'paid',
+    date: '2023-09-10',
+  },
+  {
+    customer_id: customers[5].id,
+    amount: 34577,
+    status: 'pending',
+    date: '2023-08-05',
+  },
+  {
+    customer_id: customers[7].id,
+    amount: 54246,
+    status: 'pending',
+    date: '2023-07-16',
+  },
+  {
+    customer_id: customers[6].id,
+    amount: 666,
+    status: 'pending',
+    date: '2023-06-27',
+  },
+  {
+    customer_id: customers[3].id,
+    amount: 32545,
+    status: 'paid',
+    date: '2023-06-09',
+  },
+  {
+    customer_id: customers[4].id,
+    amount: 1250,
+    status: 'paid',
+    date: '2023-06-17',
+  },
+  {
+    customer_id: customers[5].id,
+    amount: 8546,
+    status: 'paid',
+    date: '2023-06-07',
+  },
+  {
+    customer_id: customers[1].id,
+    amount: 500,
+    status: 'paid',
+    date: '2023-08-19',
+  },
+  {
+    customer_id: customers[5].id,
+    amount: 8945,
+    status: 'paid',
+    date: '2023-06-03',
+  },
+  {
+    customer_id: customers[2].id,
+    amount: 8945,
+    status: 'paid',
+    date: '2023-06-18',
+  },
+  {
+    customer_id: customers[0].id,
+    amount: 8945,
+    status: 'paid',
+    date: '2023-10-04',
+  },
+  {
+    customer_id: customers[2].id,
+    amount: 1000,
+    status: 'paid',
+    date: '2022-06-05',
+  },
+];
+
+const revenue = [
+  { month: 'Jan', revenue: 2000 },
+  { month: 'Feb', revenue: 1800 },
+  { month: 'Mar', revenue: 2200 },
+  { month: 'Apr', revenue: 2500 },
+  { month: 'May', revenue: 2300 },
+  { month: 'Jun', revenue: 3200 },
+  { month: 'Jul', revenue: 3500 },
+  { month: 'Aug', revenue: 3700 },
+  { month: 'Sep', revenue: 2500 },
+  { month: 'Oct', revenue: 2800 },
+  { month: 'Nov', revenue: 3000 },
+  { month: 'Dec', revenue: 4800 },
+];
+
+module.exports = {
+  users,
+  customers,
+  invoices,
+  revenue,
+};
diff --git a/src/bootstrap/db/seed.js b/src/bootstrap/db/seed.js
new file mode 100644
index 0000000..1b4273f
--- /dev/null
+++ b/src/bootstrap/db/seed.js
@@ -0,0 +1,188 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+const {
+  invoices,
+  customers,
+  revenue,
+  users,
+} = require('./placeholder-data.js');
+const bcrypt = require('bcrypt');
+const postgres = require('postgres');
+
+async function seedUsers(sql) {
+  try {
+    await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
+    // Create the "users" table if it doesn't exist
+    const createTable = await sql`
+      CREATE TABLE IF NOT EXISTS users (
+        id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
+        name VARCHAR(255) NOT NULL,
+        email TEXT NOT NULL UNIQUE,
+        password TEXT NOT NULL
+      );
+    `;
+
+    console.log(`Created "users" table`);
+
+    // Insert data into the "users" table
+    const insertedUsers = await Promise.all(
+      users.map(async (user) => {
+        const hashedPassword = await bcrypt.hash(user.password, 10);
+        return sql`
+        INSERT INTO users (id, name, email, password)
+        VALUES (${user.id}, ${user.name}, ${user.email}, ${hashedPassword})
+        ON CONFLICT (id) DO NOTHING;
+      `;
+      }),
+    );
+
+    console.log(`Seeded ${insertedUsers.length} users`);
+
+    return {
+      createTable,
+      users: insertedUsers,
+    };
+  } catch (error) {
+    console.error('Error seeding users:', error);
+    throw error;
+  }
+}
+
+async function seedInvoices(sql) {
+  try {
+    await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
+
+    // Create the "invoices" table if it doesn't exist
+    const createTable = await sql`
+    CREATE TABLE IF NOT EXISTS invoices (
+    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
+    customer_id UUID NOT NULL,
+    amount INT NOT NULL,
+    status VARCHAR(255) NOT NULL,
+    date DATE NOT NULL
+  );
+`;
+
+    console.log(`Created "invoices" table`);
+
+    // Insert data into the "invoices" table
+    const insertedInvoices = await Promise.all(
+      invoices.map(
+        (invoice) => sql`
+        INSERT INTO invoices (customer_id, amount, status, date)
+        VALUES (${invoice.customer_id}, ${invoice.amount}, ${invoice.status}, ${invoice.date})
+        ON CONFLICT (id) DO NOTHING;
+      `,
+      ),
+    );
+
+    console.log(`Seeded ${insertedInvoices.length} invoices`);
+
+    return {
+      createTable,
+      invoices: insertedInvoices,
+    };
+  } catch (error) {
+    console.error('Error seeding invoices:', error);
+    throw error;
+  }
+}
+
+async function seedCustomers(sql) {
+  try {
+    await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`;
+
+    // Create the "customers" table if it doesn't exist
+    const createTable = await sql`
+      CREATE TABLE IF NOT EXISTS customers (
+        id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
+        name VARCHAR(255) NOT NULL,
+        email VARCHAR(255) NOT NULL,
+        image_url VARCHAR(255) NOT NULL
+      );
+    `;
+
+    console.log(`Created "customers" table`);
+
+    // Insert data into the "customers" table
+    const insertedCustomers = await Promise.all(
+      customers.map(
+        (customer) => sql`
+        INSERT INTO customers (id, name, email, image_url)
+        VALUES (${customer.id}, ${customer.name}, ${customer.email}, ${customer.image_url})
+        ON CONFLICT (id) DO NOTHING;
+      `,
+      ),
+    );
+
+    console.log(`Seeded ${insertedCustomers.length} customers`);
+
+    return {
+      createTable,
+      customers: insertedCustomers,
+    };
+  } catch (error) {
+    console.error('Error seeding customers:', error);
+    throw error;
+  }
+}
+
+async function seedRevenue(sql) {
+  try {
+    // Create the "revenue" table if it doesn't exist
+    const createTable = await sql`
+      CREATE TABLE IF NOT EXISTS revenue (
+        month VARCHAR(4) NOT NULL UNIQUE,
+        revenue INT NOT NULL
+      );
+    `;
+
+    console.log(`Created "revenue" table`);
+
+    // Insert data into the "revenue" table
+    const insertedRevenue = await Promise.all(
+      revenue.map(
+        (rev) => sql`
+        INSERT INTO revenue (month, revenue)
+        VALUES (${rev.month}, ${rev.revenue})
+        ON CONFLICT (month) DO NOTHING;
+      `,
+      ),
+    );
+
+    console.log(`Seeded ${insertedRevenue.length} revenue`);
+
+    return {
+      createTable,
+      revenue: insertedRevenue,
+    };
+  } catch (error) {
+    console.error('Error seeding revenue:', error);
+    throw error;
+  }
+}
+
+async function main() {
+  const envs = process.env;
+  const dbConfigs = {
+    host: process.env.POSTGRES_HOST,
+    port: envs.POSTGRES_PORT,
+    username: envs.POSTGRES_USER,
+    password: envs.POSTGRES_PASS,
+    database: envs.POSTGRES_DB,
+  }
+
+  const sql = postgres(dbConfigs);
+
+  await seedUsers(sql);
+  await seedCustomers(sql);
+  await seedInvoices(sql);
+  await seedRevenue(sql);
+
+}
+
+main().catch((err) => {
+  console.error(
+    'An error occurred while attempting to seed the database:',
+    err,
+  );
+});
diff --git a/tsconfig.json b/tsconfig.json
index 714376b..d42c189 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -24,6 +24,6 @@
       "@/*": ["./src/*"]
     }
   },
-  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/bootstrap/db/seed.js", "src/bootstrap/db/placeholder-data.js"],
   "exclude": ["node_modules"]
 }
diff --git a/yarn.lock b/yarn.lock
index c6c3ff2..9b2101a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -222,6 +222,21 @@
     "@jridgewell/resolve-uri" "^3.1.0"
     "@jridgewell/sourcemap-codec" "^1.4.14"
 
+"@mapbox/node-pre-gyp@^1.0.11":
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
+  integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==
+  dependencies:
+    detect-libc "^2.0.0"
+    https-proxy-agent "^5.0.0"
+    make-dir "^3.1.0"
+    node-fetch "^2.6.7"
+    nopt "^5.0.0"
+    npmlog "^5.0.1"
+    rimraf "^3.0.2"
+    semver "^7.3.5"
+    tar "^6.1.11"
+
 "@next/env@15.0.1":
   version "15.0.1"
   resolved "https://registry.yarnpkg.com/@next/env/-/env-15.0.1.tgz#660fe9303e255cec112d3f4198d2897a24bc60b3"
@@ -450,6 +465,11 @@
   resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
   integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
 
+abbrev@1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+  integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+
 acorn-jsx@^5.3.2:
   version "5.3.2"
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
@@ -460,6 +480,13 @@ acorn@^8.9.0:
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
   integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
 
+agent-base@6:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
+  integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
+  dependencies:
+    debug "4"
+
 ajv@^6.12.4:
   version "6.12.6"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -505,6 +532,19 @@ anymatch@~3.1.2:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
 
+"aproba@^1.0.3 || ^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
+  integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
+
+are-we-there-yet@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
+  integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^3.6.0"
+
 arg@^5.0.2:
   version "5.0.2"
   resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
@@ -636,6 +676,14 @@ balanced-match@^1.0.0:
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
   integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 
+bcrypt@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2"
+  integrity sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==
+  dependencies:
+    "@mapbox/node-pre-gyp" "^1.0.11"
+    node-addon-api "^5.0.0"
+
 binary-extensions@^2.0.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
@@ -719,6 +767,11 @@ chokidar@^3.5.3:
   optionalDependencies:
     fsevents "~2.3.2"
 
+chownr@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
+  integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
+
 class-variance-authority@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522"
@@ -761,6 +814,11 @@ color-string@^1.9.0:
     color-name "^1.0.0"
     simple-swizzle "^0.2.2"
 
+color-support@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
+  integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
+
 color@^4.2.3:
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
@@ -779,6 +837,11 @@ concat-map@0.0.1:
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
   integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
 
+console-control-strings@^1.0.0, console-control-strings@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+  integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
+
 cross-spawn@^7.0.0, cross-spawn@^7.0.2:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -830,6 +893,13 @@ data-view-byte-offset@^1.0.0:
     es-errors "^1.3.0"
     is-data-view "^1.0.1"
 
+debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5:
+  version "4.3.7"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+  integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
+  dependencies:
+    ms "^2.1.3"
+
 debug@^3.2.7:
   version "3.2.7"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@@ -837,13 +907,6 @@ debug@^3.2.7:
   dependencies:
     ms "^2.1.1"
 
-debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5:
-  version "4.3.7"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
-  integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
-  dependencies:
-    ms "^2.1.3"
-
 deep-is@^0.1.3:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
@@ -867,7 +930,12 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
     has-property-descriptors "^1.0.0"
     object-keys "^1.1.1"
 
-detect-libc@^2.0.3:
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+  integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
+
+detect-libc@^2.0.0, detect-libc@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
   integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
@@ -896,6 +964,11 @@ doctrine@^3.0.0:
   dependencies:
     esutils "^2.0.2"
 
+dotenv@^16.4.5:
+  version "16.4.5"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
+  integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
+
 eastasianwidth@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
@@ -1346,6 +1419,13 @@ foreground-child@^3.1.0:
     cross-spawn "^7.0.0"
     signal-exit "^4.0.1"
 
+fs-minipass@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
+  integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
+  dependencies:
+    minipass "^3.0.0"
+
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -1376,6 +1456,21 @@ functions-have-names@^1.2.3:
   resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
   integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
 
+gauge@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395"
+  integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
+  dependencies:
+    aproba "^1.0.3 || ^2.0.0"
+    color-support "^1.1.2"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.1"
+    object-assign "^4.1.1"
+    signal-exit "^3.0.0"
+    string-width "^4.2.3"
+    strip-ansi "^6.0.1"
+    wide-align "^1.1.2"
+
 get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
@@ -1507,6 +1602,11 @@ has-tostringtag@^1.0.0, has-tostringtag@^1.0.2:
   dependencies:
     has-symbols "^1.0.3"
 
+has-unicode@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+  integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
+
 hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
@@ -1514,6 +1614,14 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
   dependencies:
     function-bind "^1.1.2"
 
+https-proxy-agent@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+  integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+  dependencies:
+    agent-base "6"
+    debug "4"
+
 ignore@^5.2.0, ignore@^5.3.1:
   version "5.3.2"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
@@ -1540,7 +1648,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2:
+inherits@2, inherits@^2.0.3:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -1898,6 +2006,13 @@ lucide-react@^0.454.0:
   resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.454.0.tgz#a81b9c482018720f07ead0503ae502d94d528444"
   integrity sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==
 
+make-dir@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
+  integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
+  dependencies:
+    semver "^6.0.0"
+
 merge2@^1.3.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@@ -1930,11 +2045,36 @@ minimist@^1.2.0, minimist@^1.2.6:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
   integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
 
+minipass@^3.0.0:
+  version "3.3.6"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
+  integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
+  dependencies:
+    yallist "^4.0.0"
+
+minipass@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d"
+  integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
+
 "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
   version "7.1.2"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
   integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
 
+minizlib@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
+  integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
+  dependencies:
+    minipass "^3.0.0"
+    yallist "^4.0.0"
+
+mkdirp@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+  integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+
 ms@^2.1.1, ms@^2.1.3:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -1982,11 +2122,40 @@ next@15.0.1:
     "@next/swc-win32-x64-msvc" "15.0.1"
     sharp "^0.33.5"
 
+node-addon-api@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
+  integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
+
+node-fetch@^2.6.7:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
+  integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
+  dependencies:
+    whatwg-url "^5.0.0"
+
+nopt@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
+  integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
+  dependencies:
+    abbrev "1"
+
 normalize-path@^3.0.0, normalize-path@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
   integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
 
+npmlog@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
+  integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==
+  dependencies:
+    are-we-there-yet "^2.0.0"
+    console-control-strings "^1.1.0"
+    gauge "^3.0.0"
+    set-blocking "^2.0.0"
+
 object-assign@^4.0.1, object-assign@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -2214,6 +2383,11 @@ postcss@^8, postcss@^8.4.23:
     picocolors "^1.1.0"
     source-map-js "^1.2.1"
 
+postgres@^3.4.5:
+  version "3.4.5"
+  resolved "https://registry.yarnpkg.com/postgres/-/postgres-3.4.5.tgz#1ef99e51b0ba9b53cbda8a215dd406725f7d15f9"
+  integrity sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==
+
 prelude-ls@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@@ -2262,6 +2436,15 @@ read-cache@^1.0.0:
   dependencies:
     pify "^2.3.0"
 
+readable-stream@^3.6.0:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
+  integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
 readdirp@~3.6.0:
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -2354,6 +2537,11 @@ safe-array-concat@^1.1.2:
     has-symbols "^1.0.3"
     isarray "^2.0.5"
 
+safe-buffer@~5.2.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
 safe-regex-test@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377"
@@ -2368,16 +2556,21 @@ scheduler@0.25.0-rc-69d4b800-20241021:
   resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-69d4b800-20241021.tgz#336e47ef2bd5eddb0ebacfd910b5df1b236d92bd"
   integrity sha512-S5AYX/YhMAN6u9AXgKYbZP4U4ZklC6R9Q7HmFSBk7d4DLiHVNxvAvlSvuM4nxFkwOk50MnpfTKQ7UWHXDOc9Eg==
 
-semver@^6.3.1:
+semver@^6.0.0, semver@^6.3.1:
   version "6.3.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
   integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
 
-semver@^7.6.0, semver@^7.6.3:
+semver@^7.3.5, semver@^7.6.0, semver@^7.6.3:
   version "7.6.3"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
   integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
 
+set-blocking@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+  integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
+
 set-function-length@^1.2.1:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
@@ -2451,6 +2644,11 @@ side-channel@^1.0.4, side-channel@^1.0.6:
     get-intrinsic "^1.2.4"
     object-inspect "^1.13.1"
 
+signal-exit@^3.0.0:
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+  integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
 signal-exit@^4.0.1:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
@@ -2482,7 +2680,7 @@ streamsearch@^1.1.0:
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.1"
 
-string-width@^4.1.0:
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.3:
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -2563,6 +2761,13 @@ string.prototype.trimstart@^1.0.8:
     define-properties "^1.2.1"
     es-object-atoms "^1.0.0"
 
+string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
 "strip-ansi-cjs@npm:strip-ansi@^6.0.1":
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
@@ -2669,6 +2874,18 @@ tapable@^2.2.0:
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
   integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
 
+tar@^6.1.11:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
+  integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
+  dependencies:
+    chownr "^2.0.0"
+    fs-minipass "^2.0.0"
+    minipass "^5.0.0"
+    minizlib "^2.1.1"
+    mkdirp "^1.0.3"
+    yallist "^4.0.0"
+
 text-table@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -2695,6 +2912,11 @@ to-regex-range@^5.0.1:
   dependencies:
     is-number "^7.0.0"
 
+tr46@~0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+  integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+
 ts-api-utils@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
@@ -2815,11 +3037,24 @@ uri-js@^4.2.2:
   dependencies:
     punycode "^2.1.0"
 
-util-deprecate@^1.0.2:
+util-deprecate@^1.0.1, util-deprecate@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
   integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
 
+webidl-conversions@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+  integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+
+whatwg-url@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+  integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
+  dependencies:
+    tr46 "~0.0.3"
+    webidl-conversions "^3.0.0"
+
 which-boxed-primitive@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
@@ -2877,6 +3112,13 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
+wide-align@^1.1.2:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
+  integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
+  dependencies:
+    string-width "^1.0.2 || 2 || 3 || 4"
+
 word-wrap@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
@@ -2905,6 +3147,11 @@ wrappy@1:
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
 yaml@^2.3.4:
   version "2.6.0"
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3"
-- 
2.39.5


From 3cfc073dd8163fed1c330f91272abe7bb5babbc6 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Thu, 31 Oct 2024 20:40:39 +0300
Subject: [PATCH 05/37] Add base of dashboard logics

---
 package.json                                  |   4 +-
 public/customers/amy-burns.png                | Bin 0 -> 7954 bytes
 public/customers/balazs-orban.png             | Bin 0 -> 7951 bytes
 public/customers/delba-de-oliveira.png        | Bin 0 -> 5824 bytes
 public/customers/emil-kowalski.png            | Bin 0 -> 4141 bytes
 public/customers/evil-rabbit.png              | Bin 0 -> 1019 bytes
 public/customers/guillermo-rauch.png          | Bin 0 -> 6284 bytes
 public/customers/hector-simpson.png           | Bin 0 -> 5602 bytes
 public/customers/jared-palmer.png             | Bin 0 -> 6460 bytes
 public/customers/lee-robinson.png             | Bin 0 -> 5653 bytes
 public/customers/michael-novotny.png          | Bin 0 -> 9902 bytes
 public/customers/steph-dietz.png              | Bin 0 -> 7151 bytes
 public/customers/steven-tey.png               | Bin 0 -> 7345 bytes
 src/app/dashboard/(overview)/page.tsx         |  15 +-
 src/app/dashboard/components/cards.tsx        |  62 +++++
 .../dashboard/components/latest-invoices.tsx  |  60 +++++
 .../dashboard/components/revenue-chart.tsx    |  53 ++++
 src/app/lib/actions.ts                        | 117 ++++++++
 src/app/lib/data.ts                           | 254 ++++++++++++++++++
 src/app/lib/definitions.ts                    |  86 ++++++
 src/app/lib/placeholder-data.js               | 188 +++++++++++++
 src/app/lib/utils.ts                          |  69 +++++
 src/bootstrap/db/db.ts                        |  13 +
 yarn.lock                                     |  10 +
 24 files changed, 925 insertions(+), 6 deletions(-)
 create mode 100644 public/customers/amy-burns.png
 create mode 100644 public/customers/balazs-orban.png
 create mode 100644 public/customers/delba-de-oliveira.png
 create mode 100644 public/customers/emil-kowalski.png
 create mode 100644 public/customers/evil-rabbit.png
 create mode 100644 public/customers/guillermo-rauch.png
 create mode 100644 public/customers/hector-simpson.png
 create mode 100644 public/customers/jared-palmer.png
 create mode 100644 public/customers/lee-robinson.png
 create mode 100644 public/customers/michael-novotny.png
 create mode 100644 public/customers/steph-dietz.png
 create mode 100644 public/customers/steven-tey.png
 create mode 100644 src/app/dashboard/components/cards.tsx
 create mode 100644 src/app/dashboard/components/latest-invoices.tsx
 create mode 100644 src/app/dashboard/components/revenue-chart.tsx
 create mode 100644 src/app/lib/actions.ts
 create mode 100644 src/app/lib/data.ts
 create mode 100644 src/app/lib/definitions.ts
 create mode 100644 src/app/lib/placeholder-data.js
 create mode 100644 src/app/lib/utils.ts
 create mode 100644 src/bootstrap/db/db.ts

diff --git a/package.json b/package.json
index b9fb46b..0cd01c1 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
     "seed": "node -r dotenv/config ./src/bootstrap/db/seed.js"
   },
   "dependencies": {
+    "@heroicons/react": "^2.1.5",
     "@radix-ui/react-icons": "^1.3.1",
     "class-variance-authority": "^0.7.0",
     "clsx": "^2.1.1",
@@ -21,7 +22,8 @@
     "reflect-metadata": "^0.2.2",
     "tailwind-merge": "^2.5.4",
     "tailwindcss-animate": "^1.0.7",
-    "tsyringe": "^4.8.0"
+    "tsyringe": "^4.8.0",
+    "zod": "^3.23.8"
   },
   "devDependencies": {
     "@types/node": "^20",
diff --git a/public/customers/amy-burns.png b/public/customers/amy-burns.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b29d72526a0644b11519442aac7d6d95f305702
GIT binary patch
literal 7954
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
IM6N<$f~UlBhyVZp

literal 0
HcmV?d00001

diff --git a/public/customers/balazs-orban.png b/public/customers/balazs-orban.png
new file mode 100644
index 0000000000000000000000000000000000000000..7fbc0097b82ad2d27141447a4ca5ef1ec68f08cf
GIT binary patch
literal 7951
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
FV1nZ*du;#!

literal 0
HcmV?d00001

diff --git a/public/customers/delba-de-oliveira.png b/public/customers/delba-de-oliveira.png
new file mode 100644
index 0000000000000000000000000000000000000000..08db1b8e854c6e3d83f431518aa8a3d2b5c250c1
GIT binary patch
literal 5824
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
KMNUMnLSTaFb|M-8

literal 0
HcmV?d00001

diff --git a/public/customers/emil-kowalski.png b/public/customers/emil-kowalski.png
new file mode 100644
index 0000000000000000000000000000000000000000..8411e219dcd6484e52f53036066be7bd14995fab
GIT binary patch
literal 4141
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

literal 0
HcmV?d00001

diff --git a/public/customers/evil-rabbit.png b/public/customers/evil-rabbit.png
new file mode 100644
index 0000000000000000000000000000000000000000..fe7990ff2f533d3d053a97f631800721e1dc64b6
GIT binary patch
literal 1019
zcmV<X0|fkuP)<h;3K|Lk000e1NJLTq001}u001}$1^@s6sD?Wp00009a7bBm000XU
z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yP<VFdsH1CU8XK~#7F?V8Oi
zd{G$3pYam%E+r{18!zQ`gRrnrO6>drWGhjmY*<)WP?D8cpp>$}N@78>@>a@1@{+fb
zmz2#k(>%X(ZjFBPa<94fe(&h&e(G%Q%z4i9Ip^GS&zw6hB4YZ(#Lyqd5wtswpxto<
z?T#bpr@p?P%FD~?w`pl<q2l6VU3EB;vbea&lD@t^j<gQCjE#*cISdXC>Zupm7yJAB
z+0W?cXjH}0)6;o=e*U}trluymU~O$J|Iggq9A{@|TP~NCmBojLhXHNp<mBK5lai7+
zn1h1@&dbZQOs=r7kS{MU1KNCieAF8!@-NHa!Q9;3D2bruDk>`Y{{B9sy^W0x>jYO;
zRyZ8j*Vj<Vva&LMdwUDp*3i(9b%Fx}1FXi$$%!(J&C1D+lCwgM=H_PW1gop7S&Ox`
zHPeE-yStig6ciL#Cn(GL`MG8S?d|P`<+{4MG-EF<Eg6ZcuR>c}n`RP6M@O8ToUA*M
z+}vEvsVS+csj*&Ami6^@%|u2<Ms&wqSXj`EIW;w9M9!FCette*TwJIo^YQV)nVFdp
z$CLM!7F%0e93LMaJwaKDii$L+l-#?zT&^GE#Ky+*+1Z(D9C^=YWMmkXGcM?tl%u7_
z_V%{2?1vMT*D5P3d4GRjwZ7~?v)e(J-_j^mRaGRCq_niOu%C-O>FVl=+-^7dd_Lux
z7&Q_L5ea$KxKB?{)Y#Zax3{-s7B*WtJ3ASP*Xu>@j37ajjW-IpqN4JG9a)H7453(F
zUM3VU!MnRVLgDdv2n9?~>_iBK+Ja>Sm|$ybE1?jN4?+PGL?$KnoP+`*C?49H&osRt
zk0)UxC{}kuAx_bhPD212K~zGLm>ArFJ0y}EJ>&>}etyE@!35=A6UFQ6E1`hRg@=a+
zLh=0kOekQ2o12@20vSsJ+0-^PG%$kq_jmL`GAs~}kB_riy1ToPa#%rGR##WqDAUu^
zXgTx0=z}ROEv3`bQ%X!s47ffqF+r)Rsg#hAKwn>9)Z5!jadC0qpUYqFb#-<0^72Ax
z;Jr3EIT<oK$H&Jkgp{)wafA_#scFp=l_e!5h4=RMLL`QVhq<SxC*=Cf%#2lX)(FZX
z&oO6bX9Gm!8>8S;I3tI|dJ6m=45p-{ggQDpNG$)fv$LaYq{Z7}5*<KdK_VecR${r}
pZ{z$=#_l+RcE=I4JC2~;`3Gq$t(-cBvWWly002ovPDHLkV1i6I<+%U=

literal 0
HcmV?d00001

diff --git a/public/customers/guillermo-rauch.png b/public/customers/guillermo-rauch.png
new file mode 100644
index 0000000000000000000000000000000000000000..e46c96f4dad4a888b7339bbf9b9a8b07df56146d
GIT binary patch
literal 6284
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
CGA(id

literal 0
HcmV?d00001

diff --git a/public/customers/hector-simpson.png b/public/customers/hector-simpson.png
new file mode 100644
index 0000000000000000000000000000000000000000..295755705963ffa4ddadc19fef3f3f3194138df2
GIT binary patch
literal 5602
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

literal 0
HcmV?d00001

diff --git a/public/customers/jared-palmer.png b/public/customers/jared-palmer.png
new file mode 100644
index 0000000000000000000000000000000000000000..e495518423acca417164517e440d970edb5ac583
GIT binary patch
literal 6460
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
WLj=Co%Z9=L0000<MNUMnLSTY04VNVV

literal 0
HcmV?d00001

diff --git a/public/customers/lee-robinson.png b/public/customers/lee-robinson.png
new file mode 100644
index 0000000000000000000000000000000000000000..633ae985b3beb0a902df9fd1a52482c1da69ca71
GIT binary patch
literal 5653
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

literal 0
HcmV?d00001

diff --git a/public/customers/michael-novotny.png b/public/customers/michael-novotny.png
new file mode 100644
index 0000000000000000000000000000000000000000..96a13a69892c1c90445cccca50ec3d28b1681971
GIT binary patch
literal 9902
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

literal 0
HcmV?d00001

diff --git a/public/customers/steph-dietz.png b/public/customers/steph-dietz.png
new file mode 100644
index 0000000000000000000000000000000000000000..e99f15aa2f7dcf15dfd86618b5da3e3ab6ffd8f3
GIT binary patch
literal 7151
zcmV<L8xZ7)P)<h;3K|Lk000e1NJLTq001}u001}$1^@s6sD?Wp00009a7bBm000XU
z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yP<VFdsH8-z(jK~#7FU0PXl
z9M^R|-P5!5tiS-oLXd<+Qj}y#krG+96qg+*j#Fhj%S)D*BrpCGc}pslJi1&?Do?3Y
zrSg=@Q?@FnEXh(5OO!-Oq{N+s0AgPT7|gzRcfRl3+dWVK4hJ*6-Lrq^JJ-2!{hbIu
z!k;h<C5l442l`p;_xM9!dLI5c4!?8kk@%c5r#fQK<%bY2R}VP$pySwUaXkK;=ju^=
zZ}Yu<rmoFChwH;D2tpYQ2J+y+WBI3l`A=C{IWK?vqdylnl@ix=#kKeK%sQq5P{3GN
zCtffX`6WRjBbcL0j*`X2i&kOm1021lRhYwtN4i)Zj^caqnfir2#wX+7cs$nob-G;{
zjYl%U=WH%32ZskTo=hZ!knL_qymU%@Kclz6?Ipl5aXtOuMGz!_#rTku(-S?(&*o$}
z8i@yOjK)Jrr_++p`H6X^c8wK7#YV=ck$LB5ELRt+S48;I<+;uoGvkP3uBT=A9t0B=
zio)z3?8wpau{4`4d2+C;&n)C~Qah<hsZh~d_+DO0m6Qb0Kt`jX-uD;py)VD`;07$6
z5^p>iNxR*V!{a0Q<jzNO+N#U){E9T&r_$|rpsAtE%~a+0zxRidO1Zjl$8{68N{mb^
zL{<RDx@YWm_8eDT6#~0X;u&H+h^I3&CeVJZR)d0v@;@KGFS%S!cJ_Cr+3CVkp7i@Y
z!7`+Fd?I;2ubz_f=&o(q+uN6NsU-UchvIoz2nY-IdOe|#cON~F{o{QZ42RIrv5bZz
z$z(H<!dfyZPY(9Cad8<M4kQDMdYLS4?n3C4-bfNxjZ`4#tVd%J6e5^F09eTLJY47L
zy#lzNm&S9vl%CTc^rTj=$?XSs<rp4SZ#JaU>4;zOr8j_;p@aZlm&QF9ix3aLu6QM1
zHnuk8{L&I!JHYyfP>zpvW-@Y6J5nJhSU|tm*9+<6d@#Qcuvl7eG#ns+;kIyJ#sq&-
zUfRSA|E6Ufn;;?^)A*jrpwnu~qx%naVapd+)csraV-+w1D_wZ%vJ}fRG8tiUqd+a%
zYPU5GsjWdD_vj3ubWg)5m(AjNp5A|ibH<ag%+6F~@!X<R=PK~Np)^ig;!%qQNP4bX
zmF)vAexf&_mK|ycck>`r{j@Igv-5EtjFYHZA<nM+&V^OEfL5y|n;RRlv;IUH5X5mp
z@pEY`DkaU6T_lJ=hTXQhE8~Cn$(p?U`nMqDEIg$stLIl`eQQ&OSUeSuy9)K^!SZx%
zD!30JvhXH)!(>dQ2XgDqZG=}Faaoj0tCuA8r+@rMf5F8(_~M=%H;!O_y3$w^3*%}6
zcL-tGL9eSmpU>r05K~f&mBJuM$_oYFSpPz9U4LIr5If<dkNZrd*Xdyq6HPc=%<k3>
zo`I-z9rcPH?%$}NNOkeNh625WuCsr9sELT)CDxO&2%TYn1S@7F<7E(56G_7ppS^NL
zu3dRX&aYgMe7-0S1b2DS?98kzFRo}-D-}x6bP<;D@Sv*r>4GkZ>MIw^s!b*W3OId5
z>eBswU+&%hTt55gBRM)eQa5$6xY=@EF0IVNON!!SA$g?r9E5YiiDXmAgc(<rxVyO_
zk8XdeO0HJt<l5z{@|CBr#bp@Q^3>uKK8={oAcXiD4ICMC9Qp3A|E65Jcu_rT(C^9#
zGO!nEAr9s0>J?<m3J&yP!4ckvG7e$(VIP6tm*u%76^5zUv2JK;va6|`F}An8BWw5X
zOYNvGGY~54IWiBS9C$>jSkw%hM(i!j;yF*R$fGA)(#QRZaHmSCAUlV3dGz^BSy{a*
zKCH<6@GJz}*xi!T;faJ!N)ukA-jQ<Im&NLW6f-5+e)2$-Wb2dkQ8y0`YtkQ%#WPxV
zaA8WGzx)h@U;!QCH9>$wPzXl>c6Se@g*bfUo4+9`bzitvih`!}#`kaEl270NP!11j
z5NbxAy}TsLvn9!dD9)bCYz9x6k-1_XG41F*mmt{s{((IF;)#r5>E-h)asValZLQ1Y
zue>bd(O7oy+)>Ym02km%Lz(d_GR~gJ7#TGfWu#Iq>-uAmLdCO&Q}K*b&sc+QhfFjY
zsDN}U#?k~?lQ$hA<uV3X4$>$O8t4uMOlub9xwk(0P-=%pxaY5~$g5YEB`<-@z!G^s
zr!nn$X{}*VFxJvhD&(b@PfHrlzxiNII?bk>KUbA@Z4U~5QM2bsy@>@oLL5;>T$Kv5
za$4`I<?E+yxv)^RsK@{XerUBiP|ThtO0Q5XY5LD00ZagWQm%ntT(l;WfHh^TiGVK2
zSQ+DKU6hkTp(yRnu^b|qoS-<D%0*c|HzTjTa7Bvao>Y+bi@pz6^R$GdvuTSnGq`_*
z+U7|PiOWkFM6+FMASs|cVX^ceE_(s@d$7I(Ap>ay9^NM?0evWaBrkmJW$B^t8|>*N
zaIjJ?$>r6HnvA^j7gmvxdQ!%M_qHCXbqL>LJW5bIhEV4gQ9z+J!aAz6-E8Byqb~aS
z$2Sx&t}HCetG~1=OEbPWLnHt!JcMW9rhta7mWasW*EF(=%Pa>iuOgj~VAUQJ9l*+A
zG=gAr^3>I9SY%#4ySpy?wYmzN0~TdjsLtl)2S50}y!G~5C~H}3MN@;n_Ufzh#_M0#
zm{-ij2N4EQuHj&)zT4}z6hU;meH_o2a$x<O8auamPG)Axa)1P|vGx$z%#(%1s;n+o
zrF(j65U54|24@<tU@3|0i(#RNAMSg8>AWn#y~luDDL0V%(E)_>r34qBsg(43<^&f0
zA%MfP*RJ9?!i9;4*%iO^Hin#+@$}hLq{joSAt`9`^0O~!2BoC5*2r3n_%gCZJ&#hM
zs8SO}B<pJrP!bMhWqDcMVgdzaGQNSMd>9Kcz`K`8+iL<C6zIo7Jbq@!L0EVJNoHg3
zKx4JrtZO+cmCN$di_gK*8IuE07Kl^7{jFcsWReFccnZ&@1^@D|e~jY>=}6?N(L6t2
zNV<T0p`cpO+NP~b+#lK=@r)JKA`s^2U=KCPg{E_o%cNx38%Q=n;z1nw>8$w3nttAg
zOL}^3)=g`?FvdEawlraF-am_kQ=Of|J$&gPb9>PcNaQJb>gg-;>T9p7>oE^dNl6ch
zpVwc1;|%aM?U=-^8h*V^093PZQ?O%!Vw4j1hw7qM$jJ6^twuxE9^QwGxH304FKv9T
zcZc%m@fVVILuAjCTv}d|*Is!+7Rm*U6=KcD4>tgekvM=rT*PSu?a<2doIDLMP=Uf(
zG7k4P5gu&_<Y^gT2jj)ftm_&f9gBihAjdShj#dZ@Cmm+el*yQZX<sNr825-lsL0(2
z%+G-)9e&5Aa%*D^5UwZZ=1RDJjJB&K$A_o#!j)y9o|3r+adYd|ZTade&r4-?2E`i5
z1_CUi*zcnRq~Wfwd}Uc$u;ytKnYUPw?K-mQq$v-8YO=_(if=8X^E}rqo<g4K#Uk6u
zsCR|az7;NN$^;8KoX$3=`~`}0OyRW6fy4-#|1&|1P&ZSs!0c=l7UP$QU6h-D{EzFB
zN9>e}zPxmCUcUbPitGaAPzhO-jVxUGt$Ukt@2CaDG(yJh%Wr@4C3#_CQF`qX>KFvA
z7Np-g2Kc_5xF<^u+cVR##4agQMj?`<Cskn~s754vJ0Gqv8uj2d11%GY7bl#Fr72?f
z#~-Hv$16w-GnEP!osle#<&nX@`pPfK5XY;?imyKVv`pH&vVC-{i}WENE8P_U;=T4`
zSwNi4!HX&dfM~pz(DxP5wUb-f`{J&gyI=&2DGsh7=39(M#tcI=4IeT2gI{@ahJauf
zOb2!yrsJW;WfU6!j-L>7NqlI4l)b!k4p!)igO+QiT#-2>lnH|X?p^|d$-~w5!5E|f
z+D8GbfEZ*uc40m*fAEc$HR}~~L__F10VvqxRiUu`#xWA%i6$f0&72`ioaFguNS!kY
z(0*kp6i>MlO(e#wPGnNM;~Jcas$*)I7EITi>qHtC?Ay{|7Fe;X!Xh}u0gkN#_~u~=
zG&$(``hZyILfow6q0S6(DLV|rZ4ZQz6ATFb(OqPu2e)S(D}X>M4D)Ed5tm2%a&Bc+
zrg<|;EUMlaeu+A;AjR4=G<~F;@w=pv<yWjDX=J{P#h<QYJ9ce8kvXfgK<p#g-r3b4
zXJ=jT3z9>aq+pR8PM;~{Q1j9<?zAKWekThdkYFKXi1XMTxZ1R`Z_7eR-j^_p*q8Nk
z&~Hr{Iu*=4!|zS$$0(Y9o0Kw93(6UP7F$QA7gMN+y_aMS*#tuoCH6ZF8NdR!Km9~}
zEK;dDXpoD|rXr2WBHJ%4wz*rA^Rqq}v8)u3eA=Bpl0_EBbLa?0Y6XIPq-k*h?Ahy$
zab2#IClQw@HqLhZOv{94C{Ff>L^+3mbfj8*;!uL^B+bg%fKB9+Df#MXd@yQRR{A}}
z*3J_d^+695D+c|bBm`r)C$cBe5vjj6=z|ngvbTRE)#5733fiy%(Gl92K^HNYlMG73
z5Dea++g7k2ARAXI1&Ltc9DxW+gyY&G?@ldh!)gc@??TzfCw2L=zxsRaJXr2nH$92j
zYU9FZ<cefk793VcgBaL*A@GMl^r)afFa=E%?D;}c6XSslC!UOP4mHfwK0lj-hO?-9
zUAeG?=5~HY(f|Qbm`7`LBv~+N1mi(uI*5xabJ9kV+uXx3P$Vf^*iE@G;X%s=nglJ#
zhD`^<u`OrDD6H3>{g`CONT%_qJys%YoU9_Pn~axy4*2oHqBM^?=q7sT)QikBs*LVn
z#DtEF=>sqNGKW}Zd&Q2zK@bjsdHEzCoNKdo2zT^xcA(&ZO3Qj>**iH!_RS=y#2}b6
z7L444kLAw*&s0hU9>#N3z$v9UwWKqJ2XZEMlOJ{h8T58DfL}gTw6k+0yZ0J$ZFNzV
zlAkHSGGNWd9tt<k1=}_80!dYU)B7={yU3Us;9uSY2**LTj^LUbJNwc_LR(lUYqyXF
zP*>n0)7tl?5OStQVZ1v81g4H?Y`W77q;0NYm@Sw#TAI!+O!17g2&dJ8S*fGglL2TT
z2Kp02^0IBO&X<v0YjV;@2e7y(K6*tEE#l2BO7{E(aViV)aP3&xf?P3c774A_+Gx*^
zs7}aSg8-ukSR4F5sm{zrY=gi=;{zm+XXW}xskla%Fag$4Ou?U-Ip}1^3d-|I-ZE>p
z&?Gn7U5yI{^}Y3dSjGeW*b`#hE35Oe3tqI{>`M9as#IUTCgtZY%LwPMZSA23qWvnv
z1(0<E63cjWt<?rwfP_&;He}o7nsGtg-x;M#GMS2z3*Si?$AlPT+RuY6P#_&ny9v|9
z+m?yoI+`TbGu;Ns$&8*egzJ$eSAU0leRAiaJbz^cb+3gOP3hF^0%EN3a9g%I$ZE3%
zagbSq9+)%)>*90gEe#l`1#dszQHHGpx}LSrpU-F<n~o-B&FCcUT^J-KU4I9*e`O7&
z@EP`?Dg;0<!804oiH@lz=7rBnKJj}!&wN&*7nVw8atZ>-O)>)?!mZxAeiQBrLI{94
z>!+o7E(dH`kRs|{c@RlCK<ws9vT*U76slQ`8FD^{;A7Ty4|MJ^K*<QuGp2IIFVQC&
zB4Kz2y@VF{2Rce)gU-J>=b+>rkj)-hGi`w=2vri;1~&?(4R2tFj-hFpI6!f*03Y3b
z5jAfxNNZdUMw|?tq8_!-T{tp>`c_?pMZB)`MonB(0D=K>DFdS}dfNJsIq0Kt{`vJ=
zTG)Lc_AxlrqsB-XI}HnRE3WB5gV0d;@x&?#FZ_}RRc};4j`;;F=y9lFtYc`wIM!&b
zX(Jq}CXL%pw179!zYP#H6>yzyyJx!kLRPiPHfpaHLimzAy)-AYm%wW>@DWokwTr^<
zxTbk-G&=HlZ3BbMCJ0|pjR348c+;@f*4~jtoIPO}x*B|(mza_qC6nIRy5mt`&at6H
zrF$fn)G{^>&vx)hcORPGDo|3GFAPLb$9U~Ls6i4_#T*78L&KU?iaBYty7C1QNg3s2
zk>o5M<`;eASb+m2VLA(tzPG!pZC9mSR+=xDiBxeuGBg2v3!}6u#tN<uT_Q75bV8dK
z%o<}di&HE|*J<R@!BGQ)mq2F?ky<I9t@EVN4pdynNjiO<&jGD8FlF7{-j;LEJq5Jk
zW7O5tY`Q#Oz_bb^7FKc$BKPj&4e7y(7nT+Q%>WRx1mBKy&|+<Dt^;Y9Ou>|1M!N-S
zM(VdWkU?{@4@!P^;i4307nEt!v~N)jn_7krfdONWIFx0Ao~P>+0um&)=U;j?nG7Y=
z9+QcUHx4a%5#x@DC_wkUy?$4=9zO)8?1Mqx*YheMeOKmySkTL**&(ot!2RgTkG2O=
zJ8i%!XoGwvCdM9UbxcNzbZd^HVKKV)X`>DL0x&Q$5da^I3l5}quni&S?P$(+5TOAk
z46A;h<Lj|zbVXVtwr&#z@E!}IX>5&qdMO_8j0GtmJ_i$^gD|BFKG@xmyC1$M_2atI
zd#BC5G#gD>0KQyZLK4BuoUT=XDN<z!iVmNQxtDf8LZy)<4W=w2p?Hi%5w!{jEzL$<
zp6s9Kz_kfphOJtJHf!_lNAk<x{GOCSY_ZPK-AyR5R798sRzO|WwEk5wlo<zElo?co
zfJ*cXou^4e1x@K#%SBKJPD(+nwk7bSd!JsH2cNz##}KXvcVgeZdr(LF)01l}6%0ra
z)XL5qE{7JQ2h0JZ=Fzotp2t>*+8?3|anlC2<#1lF!)zJ?IG{5a(!n@%?I?dUH(QpC
zdpG3Di?4}at|AlXl8(NIG1;Bl_vGV`Ka*Vya=Q4;n#od;&QOWu8fiVwBtnDXT<sG?
zvmMP$SN!mupUT5KpGgaGHH+CD0e1_o@no;AF4pPi5nGroa?qkPct;mb3-R2LZ5F3d
zc3TOp5bwvyBb_&#fi}-+WXvZIAA=KJR3I^gyY)dY4)8u4VT^a_8ChI;T0GR!<JyVb
zxbX=FG%bWrs2dhasJRm=kP{}u$;h;*IHt{K;jf<@${v!y4n|p<V6Ph|M>>Ekm$Eus
z?Dog<@#pK%R7R(+cb=SL*58z0d3IUec(JHL5-b!Di^QyH&hnAMc`hHs8J>%tM>iml
zDN`x}qn9ya3Ub$*6VMm}E2I>f?V)V!*IJnS9m`dCOe$MK{MR&VQ>k40a1!eMSnMO6
zBnP&mw?r;2xC5^9=Jj{wVEc*e9hg~q##2W{W^>^}135Us>zG?ic$+clDrt3*#R7EF
z`xE)bOKAw^OC1v@x@jht(ekRI!6T?dFXh@62#_kx4jzM5G{0$N%R-(zDlnTI!Q${Z
zZZOE943SUmLjl`+?_k1}mDMX(g~OxzX;bUu(qdMDb_e*F(`F_mFWwr(I2-{{{M*0&
zyWG5a2fh!Xk=3}6Y{t=MvOny~X=|Wai?a;Q8#vF`g<Cq|xr?)y))u8uDgk6S6<V>r
z5dwzLu&Z5qp2R*hJ<`@JogV5uzCXbj56Zl>G$UIF9o7!5ZS3nNy+FyTEY5AVCr~EF
z1~+cVi*VoH{k`8&)WZ!!#IP+0cm$fVhcSX@5KO2e=pX;+Z)6SAR#NJX_6QmXmCn<-
zU}(B+T8%|FrZF7r*35(hD5#1|$#Rbi5v#LWa*BXnI`bef_gw8InQ42RHA0DLdJsOZ
z?E$1?mQgBXWN`+p03O+KvIvKP#&e~Rf@Q%b;6Msi4+B>|zjI$czkOf6_x<0J?|kRm
z>h5s@8k4pl>>_&m$xr@H)&QAwkD6o2Q!50KivhfmCJ$yfb!pD~Lf60|NBeOko{4TA
zB9%z0fju1`>LFIy;IgLn04|7x*x>3G0mdX2dw07f&pd@|kx7eiamM5#3cil>fNoCP
zm^M!wZG&paun|XR@FH5^r~mb~Y;Nwz4}bV4mi-nbz;r9un{WPH9zR;Q2q3T<@eHDg
z2{zMzMTQ7=40L1sz@XZ+`V|aTpz$ug;9M`O<1$w>o6hYlVi_SLPXNk&;8dfuK+<Dh
zPNA3)O4QaNH!Ps&2Lnw=u1jvm(Y}ElK3k%r(~eRjW<8;jOnlaZM{Dx4H{X>XeE+)%
zAn+O(U%ma-yB5iWI&g8KGjv51BwMu{+oG61jKG`@QHxB=wAjH(S7-Me2e20Dh6T1)
zlaZ~rk;ZQ-O~~fz0#S9PJ>-2;Kt4bu@QT?PZdOP`A;6d@Gabw@IL78wIZT?|NE-#E
zCj#!FiRA5{zbn6n*<l%DY(h-;=FMBWpCq(UtP6}7*o!1dv7%$^xHnVUg3K2A(u}Ef
z+z}yYp>~c><pja5!<;bYBT-o)Ft6eCm0LuN0@CWCz5Y<{ZXB7%;D45&t=&`E**}$|
zleQclHPPi0nK)+COESWXv{yCL=Rgq?2cPe~f5WVg<F@yH@j)^Yi#MIp<s!R{%Z>M*
zIq~qtv9!LS>Nqdw-)RIr_t><-uHAWZtPf^^AKQD0Dg>5IXLSgpc$b|w?(txKPYzlm
zL;sN-AS;IZl0<4jS&`kP7?-jsjW1IkH!|_fHQxB-Q(If{*v%(f$u`D`veTySgxb``
zdk3fS7?V+Ie)~@Tg)eT+YBk&P={l^zVw=nsMUlV5VWwvpa!cB>1Gs*B4ML}_f?d6)
zk0ixsr4hqsKid>spB>P;XFM&mJWT_0M{ccD1JQbF=Fsew(e{YRgWuUbXksr+b`qNL
zjiC_mk$_!qM)ZPvJlZ@^H*;MlSx69sQ}!-l?m#*2AIg8<+|ZVdUc?Os+#i=l?Nq`^
zT|6GPM4A&~PA0aYQQ)-QBQJaB?nA?d2ewV(;F4dAY_dq0tN2FAhSw-z!DE+U(%@pq
zr*NA`9j(#YVN<tCa`wQQr=7_p=6*){&avCiKDdi<n{io_nNwyXU@|O=4HPA$s5an^
zB&=Fs`67-mVsh-!<2A#snT>Va`x5AbS?7pUxP!t3t&Rp_T}}DXFz)=rWC-GznK-T+
z%Rl_|1JuNkX_UEL#9Dj;z`$J+L^kA9-~9NoBBC<L-vT%smX+Arn%S-EaVcZ9a+J&}
zb)*oP?aw?$_tx#5IQfh9O(--Xt7d{-iBnAo#$sdrexmPDbcQmF$ySNoEfl*C){KnP
zrv4qBH07V)`8-(=<0#%7KqU;&IREd*_t9Y>s}>4c)cO1_KzL+%A1+YIu)sh`O>2&c
z(U>ZfBqoiKV{)4NeKxmtXu-gyT2ZEqy9~L2C?=vb!&+hN1|9=HBKtn=u47`BG0CL!
zvk&jd+qX8<1vI@=LS#3xcf^a=AMWF$*_X=wqC?$O9%-iawEHlTp{{1#Gf2kkrntvE
lZpv<^2(2<$)^;(s{6B<QM;hi6Bgg;%002ovPDHLkV1kang<Svu

literal 0
HcmV?d00001

diff --git a/public/customers/steven-tey.png b/public/customers/steven-tey.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f5bd7ee821107943d45e5aa29b19721a6cfae4d
GIT binary patch
literal 7345
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
XBRfi*vO-@%00000NkvXXu0mjf;hzxo

literal 0
HcmV?d00001

diff --git a/src/app/dashboard/(overview)/page.tsx b/src/app/dashboard/(overview)/page.tsx
index 60a12e0..6d632b5 100644
--- a/src/app/dashboard/(overview)/page.tsx
+++ b/src/app/dashboard/(overview)/page.tsx
@@ -1,3 +1,8 @@
+import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/components/skeleton/skeletons";
+import CardWrapper from "@/app/dashboard/components/cards";
+import LatestInvoices from "@/app/dashboard/components/latest-invoices";
+import RevenueChart from "@/app/dashboard/components/revenue-chart";
+import { Suspense } from "react";
 
 export default async function Dashboard() {
 
@@ -7,15 +12,15 @@ export default async function Dashboard() {
         Dashboard
       </h1>
       <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
-        {/* <CardWrapper /> */}
+        <CardWrapper />
       </div>
       <div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
-        {/* <Suspense fallback={<RevenueChartSkeleton />}>
+        <Suspense fallback={<RevenueChartSkeleton />}>
           <RevenueChart  />
-        </Suspense> */}
-        {/* <Suspense fallback={<LatestInvoicesSkeleton />}>
+        </Suspense>
+        <Suspense fallback={<LatestInvoicesSkeleton />}>
           <LatestInvoices />
-        </Suspense> */}
+        </Suspense>
       </div>
     </main>
   )
diff --git a/src/app/dashboard/components/cards.tsx b/src/app/dashboard/components/cards.tsx
new file mode 100644
index 0000000..72bb37b
--- /dev/null
+++ b/src/app/dashboard/components/cards.tsx
@@ -0,0 +1,62 @@
+import {
+  BanknotesIcon,
+  ClockIcon,
+  UserGroupIcon,
+  InboxIcon,
+} from '@heroicons/react/24/outline';
+import { fetchCardData } from '@/app/lib/data';
+
+const iconMap = {
+  collected: BanknotesIcon,
+  customers: UserGroupIcon,
+  pending: ClockIcon,
+  invoices: InboxIcon,
+};
+
+export default async function CardWrapper() {
+   const {
+    numberOfInvoices,
+    numberOfCustomers,
+    totalPaidInvoices,
+    totalPendingInvoices,
+  } = await fetchCardData();
+  
+  return (
+    <>
+      <Card title="Collected" value={totalPaidInvoices} type="collected" />
+      <Card title="Pending" value={totalPendingInvoices} type="pending" />
+      <Card title="Total Invoices" value={numberOfInvoices} type="invoices" />
+      <Card
+        title="Total Customers"
+        value={numberOfCustomers}
+        type="customers"
+      />
+    </>
+  );
+}
+
+export function Card({
+  title,
+  value,
+  type,
+}: {
+  title: string;
+  value: number | string;
+  type: 'invoices' | 'customers' | 'pending' | 'collected';
+}) {
+  const Icon = iconMap[type];
+
+  return (
+    <div className="rounded-xl bg-gray-50 p-2 shadow-sm">
+      <div className="flex p-4">
+        {Icon ? <Icon className="h-5 w-5 text-gray-700" /> : null}
+        <h3 className="ml-2 text-sm font-medium">{title}</h3>
+      </div>
+      <p
+        className="rounded-xl bg-white px-4 py-8 text-center text-2xl"
+      >
+        {value}
+      </p>
+    </div>
+  );
+}
diff --git a/src/app/dashboard/components/latest-invoices.tsx b/src/app/dashboard/components/latest-invoices.tsx
new file mode 100644
index 0000000..5c7cd95
--- /dev/null
+++ b/src/app/dashboard/components/latest-invoices.tsx
@@ -0,0 +1,60 @@
+import { ArrowPathIcon } from '@heroicons/react/24/outline';
+import clsx from 'clsx';
+import Image from 'next/image';
+import { fetchLatestInvoices } from '@/app/lib/data';
+export default async function LatestInvoices() {
+  const latestInvoices = await fetchLatestInvoices();
+
+  return (
+    <div className="flex w-full flex-col md:col-span-4">
+      <h2 className="mb-4 text-xl md:text-2xl">
+        Latest Invoices
+      </h2>
+      <div className="flex grow flex-col justify-between rounded-xl bg-gray-50 p-4">
+
+        <div className="bg-white px-6">
+          {latestInvoices.map((invoice, i) => {
+            return (
+              <div
+                key={invoice.id}
+                className={clsx(
+                  'flex flex-row items-center justify-between py-4',
+                  {
+                    'border-t': i !== 0,
+                  },
+                )}
+              >
+                <div className="flex items-center">
+                  <Image
+                    src={invoice.image_url}
+                    alt={`${invoice.name}'s profile picture`}
+                    className="mr-4 rounded-full"
+                    width={32}
+                    height={32}
+                  />
+                  <div className="min-w-0">
+                    <p className="truncate text-sm font-semibold md:text-base">
+                      {invoice.name}
+                    </p>
+                    <p className="hidden text-sm text-gray-500 sm:block">
+                      {invoice.email}
+                    </p>
+                  </div>
+                </div>
+                <p
+                  className="truncate text-sm font-medium md:text-base"
+                >
+                  {invoice.amount}
+                </p>
+              </div>
+            );
+          })}
+        </div>
+        <div className="flex items-center pb-2 pt-6">
+          <ArrowPathIcon className="h-5 w-5 text-gray-500" />
+          <h3 className="ml-2 text-sm text-gray-500 ">Updated just now</h3>
+        </div>
+      </div>
+    </div>
+  );
+}
diff --git a/src/app/dashboard/components/revenue-chart.tsx b/src/app/dashboard/components/revenue-chart.tsx
new file mode 100644
index 0000000..49d888c
--- /dev/null
+++ b/src/app/dashboard/components/revenue-chart.tsx
@@ -0,0 +1,53 @@
+import { generateYAxis } from '@/app/lib/utils';
+import { CalendarIcon } from '@heroicons/react/24/outline';
+import { fetchRevenue } from '@/app/lib/data';
+
+export default async function RevenueChart() {
+  const revenue = await fetchRevenue();
+
+  const chartHeight = 350;
+
+  const { yAxisLabels, topLabel } = generateYAxis(revenue);
+
+  if (!revenue || revenue.length === 0) {
+    return <p className="mt-4 text-gray-400">No data available.</p>;
+  }
+
+  return (
+    <div className="w-full md:col-span-4">
+      <h2 className={` mb-4 text-xl md:text-2xl`}>
+        Recent Revenue
+      </h2>
+      <div className="rounded-xl bg-gray-50 p-4">
+        <div className="sm:grid-cols-13 mt-0 grid grid-cols-12 items-end gap-2 rounded-md bg-white p-4 md:gap-4">
+          <div
+            className="mb-6 hidden flex-col justify-between text-sm text-gray-400 sm:flex"
+            style={{ height: `${chartHeight}px` }}
+          >
+            {yAxisLabels.map((label) => (
+              <p key={label}>{label}</p>
+            ))}
+          </div>
+
+          {revenue.map((month) => (
+            <div key={month.month} className="flex flex-col items-center gap-2">
+              <div
+                className="w-full rounded-md bg-blue-300"
+                style={{
+                  height: `${(chartHeight / topLabel) * month.revenue}px`,
+                }}
+              ></div>
+              <p className="-rotate-90 text-sm text-gray-400 sm:rotate-0">
+                {month.month}
+              </p>
+            </div>
+          ))}
+        </div>
+        <div className="flex items-center pb-2 pt-6">
+          <CalendarIcon className="h-5 w-5 text-gray-500" />
+          <h3 className="ml-2 text-sm text-gray-500 ">Last 12 months</h3>
+        </div>
+      </div>
+    </div>
+  );
+}
diff --git a/src/app/lib/actions.ts b/src/app/lib/actions.ts
new file mode 100644
index 0000000..617b4d7
--- /dev/null
+++ b/src/app/lib/actions.ts
@@ -0,0 +1,117 @@
+'use server';
+ 
+import { z } from 'zod';
+import { revalidatePath } from 'next/cache';
+import { redirect } from 'next/navigation';
+import { sql } from '@/bootstrap/db/db';
+
+const FormSchema = z.object({
+  id: z.string(),
+  customerId: z.string({
+    invalid_type_error: 'Please select a customer.',
+  }),
+  amount: z.coerce.number().gt(0, { message: 'Please enter an amount greater than $0.' }),
+  status: z.enum(['pending', 'paid'], {
+    invalid_type_error: 'Please select an invoice status.',
+  }),
+  date: z.string(),
+});
+const CreateInvoice = FormSchema.omit({ id: true, date: true });
+ 
+
+
+// This is temporary
+export type State = {
+  errors?: {
+    customerId?: string[];
+    amount?: string[];
+    status?: string[];
+  };
+  message?: string | null;
+};
+
+export async function createInvoice(_prevState: State, formData: FormData) {
+  // Validate form fields using Zod
+  console.log('ffor', formData)
+  const validatedFields = CreateInvoice.safeParse({
+    customerId: formData?.get('customerId') || undefined,
+    amount: formData?.get('amount') || undefined,
+    status: formData?.get('status') || undefined,
+  });
+
+  // If form validation fails, return errors early. Otherwise, continue.
+  if (!validatedFields.success) {
+    return {
+      errors: validatedFields.error.flatten().fieldErrors,
+      message: 'Missing Fields. Failed to Create Invoice.',
+    };
+  }
+
+  // Prepare data for insertion into the database
+  const { customerId, amount, status } = validatedFields.data;
+  const amountInCents = amount * 100;
+  const date = new Date().toISOString().split('T')[0];
+
+  // Insert data into the database
+  try {
+    await sql`
+      INSERT INTO invoices (customer_id, amount, status, date)
+      VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
+    `;
+  } catch {
+    // If a database error occurs, return a more specific error.
+    return {
+      message: 'Database Error: Failed to Create Invoice.',
+    };
+  }
+
+  // Revalidate the cache for the invoices page and redirect the user.
+  revalidatePath('/dashboard/invoices');
+  redirect('/dashboard/invoices');
+}
+
+const UpdateInvoice = FormSchema.omit({ id: true, date: true });
+export async function updateInvoice(
+  id: string,
+  _prevState: State,
+  formData: FormData,
+) {
+  const validatedFields = UpdateInvoice.safeParse({
+    customerId: formData.get('customerId'),
+    amount: formData.get('amount'),
+    status: formData.get('status'),
+  });
+ 
+  if (!validatedFields.success) {
+    return {
+      errors: validatedFields.error.flatten().fieldErrors,
+      message: 'Missing Fields. Failed to Update Invoice.',
+    };
+  }
+ 
+  const { customerId, amount, status } = validatedFields.data;
+  const amountInCents = amount * 100;
+ 
+  try {
+    await sql`
+      UPDATE invoices
+      SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status}
+      WHERE id = ${id}
+    `;
+  } catch {
+    return { message: 'Database Error: Failed to Update Invoice.' };
+  }
+ 
+  revalidatePath('/dashboard/invoices');
+  redirect('/dashboard/invoices');
+}
+export async function deleteInvoice(id: string) {
+  try {
+    await sql`DELETE FROM invoices WHERE id = ${id}`;
+    revalidatePath('/dashboard/invoices');
+    return { message: 'Deleted Invoice.' };
+  } catch {
+    return { message: 'Database Error: Failed to Delete Invoice.' };
+  } 
+}
+
diff --git a/src/app/lib/data.ts b/src/app/lib/data.ts
new file mode 100644
index 0000000..c655f10
--- /dev/null
+++ b/src/app/lib/data.ts
@@ -0,0 +1,254 @@
+import { sql } from '@/bootstrap/db/db';
+import {
+  CustomerField,
+  CustomersTableType,
+  InvoiceForm,
+  InvoicesTable,
+  User,
+  Revenue,
+  Invoice,
+  Customer,
+  LatestInvoice,
+} from './definitions';
+import { formatCurrency } from './utils';
+import postgres from 'postgres';
+import { connection } from 'next/server';
+
+export async function fetchRevenue() {
+  // This is equivalent to in fetch(..., {cache: 'no-store'}).
+  connection()
+
+  try {
+    // Artificially delay a response for demo purposes.
+    // Don't do this in production :)
+
+    console.log('Fetching revenue data...');
+    await new Promise((resolve) => setTimeout(resolve, 3000));
+
+    const data = await sql`SELECT * FROM revenue`;
+
+    console.log('Data fetch completed after 3 seconds.');
+
+    return data as postgres.RowList<Revenue[]>;
+  } catch (error) {
+    console.error('Database Error:', error);
+    throw new Error('Failed to fetch revenue data.');
+  }
+}
+
+export async function fetchLatestInvoices() {
+  // This is equivalent to in fetch(..., {cache: 'no-store'}).
+  connection()
+
+  try {
+    const data = await sql`
+      SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
+      FROM invoices
+      JOIN customers ON invoices.customer_id = customers.id
+      ORDER BY invoices.date DESC
+      LIMIT 5` as postgres.RowList<LatestInvoice[]>;
+
+    const latestInvoices = data.map((invoice) => ({
+      ...invoice,
+      amount: formatCurrency(+invoice.amount),
+    }));
+    return latestInvoices;
+  } catch (error) {
+    console.error('Database Error:', error);
+    throw new Error('Failed to fetch the latest invoices.');
+  }
+}
+
+export async function fetchCardData() {
+    // This is equivalent to in fetch(..., {cache: 'no-store'}).
+    connection()
+
+  try {
+    // You can probably combine these into a single SQL query
+    // However, we are intentionally splitting them to demonstrate
+    // how to initialize multiple queries in parallel with JS.
+    const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
+    const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
+    const invoiceStatusPromise = sql`SELECT
+         SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
+         SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
+         FROM invoices`;
+
+    const data = await Promise.all([
+      invoiceCountPromise,
+      customerCountPromise,
+      invoiceStatusPromise,
+    ]);
+
+    const invoices = data[0] as postgres.RowList<Invoice[]>
+    const customres = data[1] as postgres.RowList<Customer[]>
+    const invoiceStatus = data[2] as postgres.RowList<({paid: string, pending: string})[]>
+    const numberOfInvoices = Number(invoices.count ?? '0');
+    const numberOfCustomers = Number(customres.count ?? '0');
+    const totalPaidInvoices = formatCurrency(Number(invoiceStatus.at(0)?.paid ?? '0'));
+    const totalPendingInvoices = formatCurrency(Number(invoiceStatus.at(0)?.pending ?? '0'));
+
+    return {
+      numberOfCustomers,
+      numberOfInvoices,
+      totalPaidInvoices,
+      totalPendingInvoices,
+    };
+  } catch (error) {
+    console.error('Database Error:', error);
+    throw new Error('Failed to fetch card data.');
+  }
+}
+
+const ITEMS_PER_PAGE = 6;
+export async function fetchFilteredInvoices(
+  query: string,
+  currentPage: number,
+) {
+    // This is equivalent to in fetch(..., {cache: 'no-store'}).
+  connection()
+
+  const offset = (currentPage - 1) * ITEMS_PER_PAGE;
+
+  try {
+    const invoices = await sql`
+      SELECT
+        invoices.id,
+        invoices.amount,
+        invoices.date,
+        invoices.status,
+        customers.name,
+        customers.email,
+        customers.image_url
+      FROM invoices
+      JOIN customers ON invoices.customer_id = customers.id
+      WHERE
+        customers.name ILIKE ${`%${query}%`} OR
+        customers.email ILIKE ${`%${query}%`} OR
+        invoices.amount::text ILIKE ${`%${query}%`} OR
+        invoices.date::text ILIKE ${`%${query}%`} OR
+        invoices.status ILIKE ${`%${query}%`}
+      ORDER BY invoices.date DESC
+      LIMIT ${ITEMS_PER_PAGE} OFFSET ${offset}
+    ` as postgres.RowList<InvoicesTable[]>;
+
+    return invoices;
+  } catch (error) {
+    console.error('Database Error:', error);
+    throw new Error('Failed to fetch invoices.');
+  }
+}
+
+export async function fetchInvoicesPages(query: string) {
+    // This is equivalent to in fetch(..., {cache: 'no-store'}).
+    connection()
+  try {
+    const count = await sql`SELECT COUNT(*)
+    FROM invoices
+    JOIN customers ON invoices.customer_id = customers.id
+    WHERE
+      customers.name ILIKE ${`%${query}%`} OR
+      customers.email ILIKE ${`%${query}%`} OR
+      invoices.amount::text ILIKE ${`%${query}%`} OR
+      invoices.date::text ILIKE ${`%${query}%`} OR
+      invoices.status ILIKE ${`%${query}%`}
+  `;
+
+    const totalPages = Math.ceil(Number(count.at(0)?.count) / ITEMS_PER_PAGE);
+    return totalPages;
+  } catch (error) {
+    console.error('Database Error:', error);
+    throw new Error('Failed to fetch total number of invoices.');
+  }
+}
+
+export async function fetchInvoiceById(id: string) {
+    // This is equivalent to in fetch(..., {cache: 'no-store'}).
+    connection()
+  try {
+    const data = await sql`
+      SELECT
+        invoices.id,
+        invoices.customer_id,
+        invoices.amount,
+        invoices.status
+      FROM invoices
+      WHERE invoices.id = ${id};
+    ` as postgres.RowList<InvoiceForm[]>;
+
+    const invoice = data.map((invoice) => ({
+      ...invoice,
+      // Convert amount from cents to dollars
+      amount: invoice.amount / 100,
+    }));
+
+    return invoice[0];
+  } catch (error) {
+    console.error('Database Error:', error);
+    throw new Error('Failed to fetch invoice.');
+  }
+}
+
+export async function fetchCustomers() {
+    // This is equivalent to in fetch(..., {cache: 'no-store'}).
+    connection()
+  try {
+    const data = await sql`
+      SELECT
+        id,
+        name
+      FROM customers
+      ORDER BY name ASC
+    ` as postgres.RowList<CustomerField[]>;
+
+    return data;
+  } catch (err) {
+    console.error('Database Error:', err);
+    throw new Error('Failed to fetch all customers.');
+  }
+}
+
+export async function fetchFilteredCustomers(query: string) {
+    // This is equivalent to in fetch(..., {cache: 'no-store'}).
+    connection()
+  try {
+    const data = await sql`
+		SELECT
+		  customers.id,
+		  customers.name,
+		  customers.email,
+		  customers.image_url,
+		  COUNT(invoices.id) AS total_invoices,
+		  SUM(CASE WHEN invoices.status = 'pending' THEN invoices.amount ELSE 0 END) AS total_pending,
+		  SUM(CASE WHEN invoices.status = 'paid' THEN invoices.amount ELSE 0 END) AS total_paid
+		FROM customers
+		LEFT JOIN invoices ON customers.id = invoices.customer_id
+		WHERE
+		  customers.name ILIKE ${`%${query}%`} OR
+        customers.email ILIKE ${`%${query}%`}
+		GROUP BY customers.id, customers.name, customers.email, customers.image_url
+		ORDER BY customers.name ASC
+	  ` as postgres.RowList<CustomersTableType[]>;
+
+    const customers = data.map((customer) => ({
+      ...customer,
+      total_pending: formatCurrency(customer.total_pending),
+      total_paid: formatCurrency(customer.total_paid),
+    }));
+
+    return customers;
+  } catch (err) {
+    console.error('Database Error:', err);
+    throw new Error('Failed to fetch customer table.');
+  }
+}
+
+export async function getUser(email: string) {
+  try {
+    const user = await sql`SELECT * FROM users WHERE email=${email}`;
+    return user.at(0) as User;
+  } catch (error) {
+    console.error('Failed to fetch user:', error);
+    throw new Error('Failed to fetch user.');
+  }
+}
diff --git a/src/app/lib/definitions.ts b/src/app/lib/definitions.ts
new file mode 100644
index 0000000..e439b66
--- /dev/null
+++ b/src/app/lib/definitions.ts
@@ -0,0 +1,86 @@
+// This file contains type definitions for your data.
+// It describes the shape of the data, and what data type each property should accept.
+// For simplicity of teaching, we're manually defining these types.
+// However, these types are generated automatically if you're using an ORM such as Prisma.
+export type User = {
+  id: string;
+  name: string;
+  email: string;
+  password: string;
+};
+
+export type Customer = {
+  id: string;
+  name: string;
+  email: string;
+  image_url: string;
+};
+
+export type Invoice = {
+  id: string; // Will be created on the database
+  customer_id: string;
+  amount: number; // Stored in cents
+  status: 'pending' | 'paid';
+  date: string;
+};
+
+export type Revenue = {
+  month: string;
+  revenue: number;
+};
+
+export type LatestInvoice = {
+  id: string;
+  name: string;
+  image_url: string;
+  email: string;
+  amount: string;
+};
+
+// The database returns a number for amount, but we later format it to a string with the formatCurrency function
+export type LatestInvoiceRaw = Omit<LatestInvoice, 'amount'> & {
+  amount: number;
+};
+
+export type InvoicesTable = {
+  id: string;
+  customer_id: string;
+  name: string;
+  email: string;
+  image_url: string;
+  date: string;
+  amount: number;
+  status: 'pending' | 'paid';
+};
+
+export type CustomersTableType = {
+  id: string;
+  name: string;
+  email: string;
+  image_url: string;
+  total_invoices: number;
+  total_pending: number;
+  total_paid: number;
+};
+
+export type FormattedCustomersTable = {
+  id: string;
+  name: string;
+  email: string;
+  image_url: string;
+  total_invoices: number;
+  total_pending: string;
+  total_paid: string;
+};
+
+export type CustomerField = {
+  id: string;
+  name: string;
+};
+
+export type InvoiceForm = {
+  id: string;
+  customer_id: string;
+  amount: number;
+  status: 'pending' | 'paid';
+};
diff --git a/src/app/lib/placeholder-data.js b/src/app/lib/placeholder-data.js
new file mode 100644
index 0000000..15a4156
--- /dev/null
+++ b/src/app/lib/placeholder-data.js
@@ -0,0 +1,188 @@
+// This file contains placeholder data that you'll be replacing with real data in the Data Fetching chapter:
+// https://nextjs.org/learn/dashboard-app/fetching-data
+const users = [
+  {
+    id: '410544b2-4001-4271-9855-fec4b6a6442a',
+    name: 'User',
+    email: 'user@nextmail.com',
+    password: '123456',
+  },
+];
+
+const customers = [
+  {
+    id: '3958dc9e-712f-4377-85e9-fec4b6a6442a',
+    name: 'Delba de Oliveira',
+    email: 'delba@oliveira.com',
+    image_url: '/customers/delba-de-oliveira.png',
+  },
+  {
+    id: '3958dc9e-742f-4377-85e9-fec4b6a6442a',
+    name: 'Lee Robinson',
+    email: 'lee@robinson.com',
+    image_url: '/customers/lee-robinson.png',
+  },
+  {
+    id: '3958dc9e-737f-4377-85e9-fec4b6a6442a',
+    name: 'Hector Simpson',
+    email: 'hector@simpson.com',
+    image_url: '/customers/hector-simpson.png',
+  },
+  {
+    id: '50ca3e18-62cd-11ee-8c99-0242ac120002',
+    name: 'Steven Tey',
+    email: 'steven@tey.com',
+    image_url: '/customers/steven-tey.png',
+  },
+  {
+    id: '3958dc9e-787f-4377-85e9-fec4b6a6442a',
+    name: 'Steph Dietz',
+    email: 'steph@dietz.com',
+    image_url: '/customers/steph-dietz.png',
+  },
+  {
+    id: '76d65c26-f784-44a2-ac19-586678f7c2f2',
+    name: 'Michael Novotny',
+    email: 'michael@novotny.com',
+    image_url: '/customers/michael-novotny.png',
+  },
+  {
+    id: 'd6e15727-9fe1-4961-8c5b-ea44a9bd81aa',
+    name: 'Evil Rabbit',
+    email: 'evil@rabbit.com',
+    image_url: '/customers/evil-rabbit.png',
+  },
+  {
+    id: '126eed9c-c90c-4ef6-a4a8-fcf7408d3c66',
+    name: 'Emil Kowalski',
+    email: 'emil@kowalski.com',
+    image_url: '/customers/emil-kowalski.png',
+  },
+  {
+    id: 'CC27C14A-0ACF-4F4A-A6C9-D45682C144B9',
+    name: 'Amy Burns',
+    email: 'amy@burns.com',
+    image_url: '/customers/amy-burns.png',
+  },
+  {
+    id: '13D07535-C59E-4157-A011-F8D2EF4E0CBB',
+    name: 'Balazs Orban',
+    email: 'balazs@orban.com',
+    image_url: '/customers/balazs-orban.png',
+  },
+];
+
+const invoices = [
+  {
+    customer_id: customers[0].id,
+    amount: 15795,
+    status: 'pending',
+    date: '2022-12-06',
+  },
+  {
+    customer_id: customers[1].id,
+    amount: 20348,
+    status: 'pending',
+    date: '2022-11-14',
+  },
+  {
+    customer_id: customers[4].id,
+    amount: 3040,
+    status: 'paid',
+    date: '2022-10-29',
+  },
+  {
+    customer_id: customers[3].id,
+    amount: 44800,
+    status: 'paid',
+    date: '2023-09-10',
+  },
+  {
+    customer_id: customers[5].id,
+    amount: 34577,
+    status: 'pending',
+    date: '2023-08-05',
+  },
+  {
+    customer_id: customers[7].id,
+    amount: 54246,
+    status: 'pending',
+    date: '2023-07-16',
+  },
+  {
+    customer_id: customers[6].id,
+    amount: 666,
+    status: 'pending',
+    date: '2023-06-27',
+  },
+  {
+    customer_id: customers[3].id,
+    amount: 32545,
+    status: 'paid',
+    date: '2023-06-09',
+  },
+  {
+    customer_id: customers[4].id,
+    amount: 1250,
+    status: 'paid',
+    date: '2023-06-17',
+  },
+  {
+    customer_id: customers[5].id,
+    amount: 8546,
+    status: 'paid',
+    date: '2023-06-07',
+  },
+  {
+    customer_id: customers[1].id,
+    amount: 500,
+    status: 'paid',
+    date: '2023-08-19',
+  },
+  {
+    customer_id: customers[5].id,
+    amount: 8945,
+    status: 'paid',
+    date: '2023-06-03',
+  },
+  {
+    customer_id: customers[2].id,
+    amount: 8945,
+    status: 'paid',
+    date: '2023-06-18',
+  },
+  {
+    customer_id: customers[0].id,
+    amount: 8945,
+    status: 'paid',
+    date: '2023-10-04',
+  },
+  {
+    customer_id: customers[2].id,
+    amount: 1000,
+    status: 'paid',
+    date: '2022-06-05',
+  },
+];
+
+const revenue = [
+  { month: 'Jan', revenue: 2000 },
+  { month: 'Feb', revenue: 1800 },
+  { month: 'Mar', revenue: 2200 },
+  { month: 'Apr', revenue: 2500 },
+  { month: 'May', revenue: 2300 },
+  { month: 'Jun', revenue: 3200 },
+  { month: 'Jul', revenue: 3500 },
+  { month: 'Aug', revenue: 3700 },
+  { month: 'Sep', revenue: 2500 },
+  { month: 'Oct', revenue: 2800 },
+  { month: 'Nov', revenue: 3000 },
+  { month: 'Dec', revenue: 4800 },
+];
+
+module.exports = {
+  users,
+  customers,
+  invoices,
+  revenue,
+};
diff --git a/src/app/lib/utils.ts b/src/app/lib/utils.ts
new file mode 100644
index 0000000..b7f7cff
--- /dev/null
+++ b/src/app/lib/utils.ts
@@ -0,0 +1,69 @@
+import { Revenue } from './definitions';
+
+export const formatCurrency = (amount: number) => {
+  return (amount / 100).toLocaleString('en-US', {
+    style: 'currency',
+    currency: 'USD',
+  });
+};
+
+export const formatDateToLocal = (
+  dateStr: string,
+  locale: string = 'en-US',
+) => {
+  const date = new Date(dateStr);
+  const options: Intl.DateTimeFormatOptions = {
+    day: 'numeric',
+    month: 'short',
+    year: 'numeric',
+  };
+  const formatter = new Intl.DateTimeFormat(locale, options);
+  return formatter.format(date);
+};
+
+export const generateYAxis = (revenue: Revenue[]) => {
+  // Calculate what labels we need to display on the y-axis
+  // based on highest record and in 1000s
+  const yAxisLabels = [];
+  const highestRecord = Math.max(...revenue.map((month) => month.revenue));
+  const topLabel = Math.ceil(highestRecord / 1000) * 1000;
+
+  for (let i = topLabel; i >= 0; i -= 1000) {
+    yAxisLabels.push(`$${i / 1000}K`);
+  }
+
+  return { yAxisLabels, topLabel };
+};
+
+export const generatePagination = (currentPage: number, totalPages: number) => {
+  // If the total number of pages is 7 or less,
+  // display all pages without any ellipsis.
+  if (totalPages <= 7) {
+    return Array.from({ length: totalPages }, (_, i) => i + 1);
+  }
+
+  // If the current page is among the first 3 pages,
+  // show the first 3, an ellipsis, and the last 2 pages.
+  if (currentPage <= 3) {
+    return [1, 2, 3, '...', totalPages - 1, totalPages];
+  }
+
+  // If the current page is among the last 3 pages,
+  // show the first 2, an ellipsis, and the last 3 pages.
+  if (currentPage >= totalPages - 2) {
+    return [1, 2, '...', totalPages - 2, totalPages - 1, totalPages];
+  }
+
+  // If the current page is somewhere in the middle,
+  // show the first page, an ellipsis, the current page and its neighbors,
+  // another ellipsis, and the last page.
+  return [
+    1,
+    '...',
+    currentPage - 1,
+    currentPage,
+    currentPage + 1,
+    '...',
+    totalPages,
+  ];
+};
diff --git a/src/bootstrap/db/db.ts b/src/bootstrap/db/db.ts
new file mode 100644
index 0000000..dbcd877
--- /dev/null
+++ b/src/bootstrap/db/db.ts
@@ -0,0 +1,13 @@
+import postgres from "postgres";
+
+
+const envs = process.env;
+const dbConfigs = {
+    host: envs.POSTGRES_HOST,
+    port: Number(envs.POSTGRES_PORT),
+    username: envs.POSTGRES_USER,
+    password: envs.POSTGRES_PASS,
+    database: envs.POSTGRES_DB,
+}
+
+export const sql = postgres(dbConfigs);
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 9b2101a..ee61d15 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -46,6 +46,11 @@
   resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
   integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
 
+"@heroicons/react@^2.1.5":
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.5.tgz#1e13f34976cc542deae92353c01c8b3d7942e9ba"
+  integrity sha512-FuzFN+BsHa+7OxbvAERtgBTNeZpUjgM/MIizfVkSCL2/edriN0Hx/DWRCR//aPYwO5QX/YlgLGXk+E3PcfZwjA==
+
 "@humanwhocodes/config-array@^0.13.0":
   version "0.13.0"
   resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748"
@@ -3161,3 +3166,8 @@ yocto-queue@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
   integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+zod@^3.23.8:
+  version "3.23.8"
+  resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
+  integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
-- 
2.39.5


From a9bb5883656e5c94611af06cc8a2bd669f47e7ef Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 12:39:02 +0300
Subject: [PATCH 06/37] Update next to latest

---
 package.json |   2 +-
 yarn.lock    | 110 +++++++++++++++++++++++++--------------------------
 2 files changed, 56 insertions(+), 56 deletions(-)

diff --git a/package.json b/package.json
index 0cd01c1..a804e40 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
     "class-variance-authority": "^0.7.0",
     "clsx": "^2.1.1",
     "lucide-react": "^0.454.0",
-    "next": "15.0.1",
+    "next": "15.0.2",
     "postgres": "^3.4.5",
     "react": "19.0.0-rc-69d4b800-20241021",
     "react-dom": "19.0.0-rc-69d4b800-20241021",
diff --git a/yarn.lock b/yarn.lock
index ee61d15..80c25e6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -242,10 +242,10 @@
     semver "^7.3.5"
     tar "^6.1.11"
 
-"@next/env@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/env/-/env-15.0.1.tgz#660fe9303e255cec112d3f4198d2897a24bc60b3"
-  integrity sha512-lc4HeDUKO9gxxlM5G2knTRifqhsY6yYpwuHspBZdboZe0Gp+rZHBNNSIjmQKDJIdRXiXGyVnSD6gafrbQPvILQ==
+"@next/env@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/env/-/env-15.0.2.tgz#4e921af3faf8a16c6be98ec6a81a32a40050a8b7"
+  integrity sha512-c0Zr0ModK5OX7D4ZV8Jt/wqoXtitLNPwUfG9zElCZztdaZyNVnN40rDXVZ/+FGuR4CcNV5AEfM6N8f+Ener7Dg==
 
 "@next/eslint-plugin-next@15.0.1":
   version "15.0.1"
@@ -254,45 +254,45 @@
   dependencies:
     fast-glob "3.3.1"
 
-"@next/swc-darwin-arm64@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.1.tgz#b80a25f1569bd0ca03eca9473f7e93e64937e404"
-  integrity sha512-C9k/Xv4sxkQRTA37Z6MzNq3Yb1BJMmSqjmwowoWEpbXTkAdfOwnoKOpAb71ItSzoA26yUTIo6ZhN8rKGu4ExQw==
+"@next/swc-darwin-arm64@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.2.tgz#66f84083f1f564d09bbacff8d6b24bd833783bef"
+  integrity sha512-GK+8w88z+AFlmt+ondytZo2xpwlfAR8U6CRwXancHImh6EdGfHMIrTSCcx5sOSBei00GyLVL0ioo1JLKTfprgg==
 
-"@next/swc-darwin-x64@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.1.tgz#00dcf79ec7c638a85c3b9ff2e2de2bfb09c1c250"
-  integrity sha512-uHl13HXOuq1G7ovWFxCACDJHTSDVbn/sbLv8V1p+7KIvTrYQ5HNoSmKBdYeEKRRCbEmd+OohOgg9YOp8Ux3MBg==
+"@next/swc-darwin-x64@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.2.tgz#1aef085642f363b89acf264cf1b9848632b52914"
+  integrity sha512-KUpBVxIbjzFiUZhiLIpJiBoelqzQtVZbdNNsehhUn36e2YzKHphnK8eTUW1s/4aPy5kH/UTid8IuVbaOpedhpw==
 
-"@next/swc-linux-arm64-gnu@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.1.tgz#faab5f7ffcc6d1a15e8dea1cb9953966658b39bf"
-  integrity sha512-LvyhvxHOihFTEIbb35KxOc3q8w8G4xAAAH/AQnsYDEnOvwawjL2eawsB59AX02ki6LJdgDaHoTEnC54Gw+82xw==
+"@next/swc-linux-arm64-gnu@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.2.tgz#203b41742e60642587e004773a8c203053b6832e"
+  integrity sha512-9J7TPEcHNAZvwxXRzOtiUvwtTD+fmuY0l7RErf8Yyc7kMpE47MIQakl+3jecmkhOoIyi/Rp+ddq7j4wG6JDskQ==
 
-"@next/swc-linux-arm64-musl@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.1.tgz#97abada9a782ab5b3cb42cf0d4799cbc2e733351"
-  integrity sha512-vFmCGUFNyk/A5/BYcQNhAQqPIw01RJaK6dRO+ZEhz0DncoW+hJW1kZ8aH2UvTX27zPq3m85zN5waMSbZEmANcQ==
+"@next/swc-linux-arm64-musl@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.2.tgz#d256932ec11051f376348862508be9017b23f3d8"
+  integrity sha512-BjH4ZSzJIoTTZRh6rG+a/Ry4SW0HlizcPorqNBixBWc3wtQtj4Sn9FnRZe22QqrPnzoaW0ctvSz4FaH4eGKMww==
 
-"@next/swc-linux-x64-gnu@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.1.tgz#548bd47c49fe6d819302139aff8766eb704322e2"
-  integrity sha512-5by7IYq0NCF8rouz6Qg9T97jYU68kaClHPfGpQG2lCZpSYHtSPQF1kjnqBTd34RIqPKMbCa4DqCufirgr8HM5w==
+"@next/swc-linux-x64-gnu@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.2.tgz#06c52a23a7e13d5ccd0ded1cf295b32df58e0932"
+  integrity sha512-i3U2TcHgo26sIhcwX/Rshz6avM6nizrZPvrDVDY1bXcLH1ndjbO8zuC7RoHp0NSK7wjJMPYzm7NYL1ksSKFreA==
 
-"@next/swc-linux-x64-musl@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.1.tgz#84423fbd3a058dd6ae8322e530878f0ec7a1027a"
-  integrity sha512-lmYr6H3JyDNBJLzklGXLfbehU3ay78a+b6UmBGlHls4xhDXBNZfgb0aI67sflrX+cGBnv1LgmWzFlYrAYxS1Qw==
+"@next/swc-linux-x64-musl@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.2.tgz#eb70a81a1c66d4935d50bf6fe1021e440f27fe9f"
+  integrity sha512-AMfZfSVOIR8fa+TXlAooByEF4OB00wqnms1sJ1v+iu8ivwvtPvnkwdzzFMpsK5jA2S9oNeeQ04egIWVb4QWmtQ==
 
-"@next/swc-win32-arm64-msvc@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.1.tgz#723c2ced12a998fb40dc901b8faea9170e788c2f"
-  integrity sha512-DS8wQtl6diAj0eZTdH0sefykm4iXMbHT4MOvLwqZiIkeezKpkgPFcEdFlz3vKvXa2R/2UEgMh48z1nEpNhjeOQ==
+"@next/swc-win32-arm64-msvc@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.2.tgz#29a763bdc3a1281633af10cf8428e916e02f079a"
+  integrity sha512-JkXysDT0/hEY47O+Hvs8PbZAeiCQVxKfGtr4GUpNAhlG2E0Mkjibuo8ryGD29Qb5a3IOnKYNoZlh/MyKd2Nbww==
 
-"@next/swc-win32-x64-msvc@15.0.1":
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.1.tgz#ec7e3befc0bcc47527537b1eda2b3745beb15a09"
-  integrity sha512-4Ho2ggvDdMKlZ/0e9HNdZ9ngeaBwtc+2VS5oCeqrbXqOgutX6I4U2X/42VBw0o+M5evn4/7v3zKgGHo+9v/VjA==
+"@next/swc-win32-x64-msvc@15.0.2":
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.2.tgz#0f70d8146990886a85099875353539fc6dd68338"
+  integrity sha512-foaUL0NqJY/dX0Pi/UcZm5zsmSk5MtP/gxx3xOPyREkMFN+CTjctPfu3QaqrQHinaKdPnMWPJDKt4VjDfTBe/Q==
 
 "@nodelib/fs.scandir@2.1.5":
   version "2.1.5"
@@ -745,9 +745,9 @@ camelcase-css@^2.0.1:
   integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
 
 caniuse-lite@^1.0.30001579:
-  version "1.0.30001674"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001674.tgz#eb200a716c3e796d33d30b9c8890517a72f862c8"
-  integrity sha512-jOsKlZVRnzfhLojb+Ykb+gyUSp9Xb57So+fAiFlLzzTKpqg8xxSav0e40c8/4F/v9N8QSvrRRaLeVzQbLqomYw==
+  version "1.0.30001676"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz#fe133d41fe74af8f7cc93b8a714c3e86a86e6f04"
+  integrity sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==
 
 chalk@^4.0.0:
   version "4.1.2"
@@ -2104,12 +2104,12 @@ natural-compare@^1.4.0:
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
   integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
 
-next@15.0.1:
-  version "15.0.1"
-  resolved "https://registry.yarnpkg.com/next/-/next-15.0.1.tgz#a0e8eda35d803cb7f8092b2a2eb9d072e22bf21d"
-  integrity sha512-PSkFkr/w7UnFWm+EP8y/QpHrJXMqpZzAXpergB/EqLPOh4SGPJXv1wj4mslr2hUZBAS9pX7/9YLIdxTv6fwytw==
+next@15.0.2:
+  version "15.0.2"
+  resolved "https://registry.yarnpkg.com/next/-/next-15.0.2.tgz#4a2224c007856118010b8cef5e9b2383cd743388"
+  integrity sha512-rxIWHcAu4gGSDmwsELXacqAPUk+j8dV/A9cDF5fsiCMpkBDYkO2AEaL1dfD+nNmDiU6QMCFN8Q30VEKapT9UHQ==
   dependencies:
-    "@next/env" "15.0.1"
+    "@next/env" "15.0.2"
     "@swc/counter" "0.1.3"
     "@swc/helpers" "0.5.13"
     busboy "1.6.0"
@@ -2117,14 +2117,14 @@ next@15.0.1:
     postcss "8.4.31"
     styled-jsx "5.1.6"
   optionalDependencies:
-    "@next/swc-darwin-arm64" "15.0.1"
-    "@next/swc-darwin-x64" "15.0.1"
-    "@next/swc-linux-arm64-gnu" "15.0.1"
-    "@next/swc-linux-arm64-musl" "15.0.1"
-    "@next/swc-linux-x64-gnu" "15.0.1"
-    "@next/swc-linux-x64-musl" "15.0.1"
-    "@next/swc-win32-arm64-msvc" "15.0.1"
-    "@next/swc-win32-x64-msvc" "15.0.1"
+    "@next/swc-darwin-arm64" "15.0.2"
+    "@next/swc-darwin-x64" "15.0.2"
+    "@next/swc-linux-arm64-gnu" "15.0.2"
+    "@next/swc-linux-arm64-musl" "15.0.2"
+    "@next/swc-linux-x64-gnu" "15.0.2"
+    "@next/swc-linux-x64-musl" "15.0.2"
+    "@next/swc-win32-arm64-msvc" "15.0.2"
+    "@next/swc-win32-x64-msvc" "15.0.2"
     sharp "^0.33.5"
 
 node-addon-api@^5.0.0:
@@ -2948,9 +2948,9 @@ tslib@^1.9.3:
   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
 tslib@^2.4.0:
-  version "2.8.0"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b"
-  integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==
+  version "2.8.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
 
 tsyringe@^4.8.0:
   version "4.8.0"
-- 
2.39.5


From 7b2ec83068c5ad05b99d12d15c0fb00394cacee9 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 14:54:59 +0300
Subject: [PATCH 07/37] Add domain of customer

---
 package.json                                  |   10 +-
 src/app/lib/data.ts                           |   31 -
 src/app/lib/definitions.ts                    |   16 -
 src/feature/common/server-di.ts               |    5 +-
 src/feature/customer/customer-key.ts          |    1 +
 .../customer/data/module/customer-di.ts       |   13 +
 .../customer/domain/entity/customer.ts        |   37 +
 .../customer/domain/i-repo/customer-repo.ts   |    7 +
 .../domain/usecase/fetch-customers-usecase.ts |   12 +
 .../customer/customer-fake-factory.ts         |   21 +
 src/test/common/mock/mock-di.ts               |    7 +
 src/test/common/mock/mock-factory.ts          |    5 +
 src/test/setup.ts                             |    1 +
 .../usecase/fetch-customers-usecase.test.ts   |   48 +
 tsconfig.json                                 |    1 +
 vite.config.ts                                |   15 +
 yarn.lock                                     | 1125 ++++++++++++++++-
 17 files changed, 1291 insertions(+), 64 deletions(-)
 create mode 100644 src/feature/customer/customer-key.ts
 create mode 100644 src/feature/customer/data/module/customer-di.ts
 create mode 100644 src/feature/customer/domain/entity/customer.ts
 create mode 100644 src/feature/customer/domain/i-repo/customer-repo.ts
 create mode 100644 src/feature/customer/domain/usecase/fetch-customers-usecase.ts
 create mode 100644 src/test/common/fake-factory/customer/customer-fake-factory.ts
 create mode 100644 src/test/common/mock/mock-di.ts
 create mode 100644 src/test/common/mock/mock-factory.ts
 create mode 100644 src/test/setup.ts
 create mode 100644 src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
 create mode 100644 vite.config.ts

diff --git a/package.json b/package.json
index a804e40..2a11c38 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
     "build": "next build",
     "start": "next start --port 4000",
     "lint": "next lint",
+    "test": "vitest",
     "seed": "node -r dotenv/config ./src/bootstrap/db/seed.js"
   },
   "dependencies": {
@@ -26,15 +27,22 @@
     "zod": "^3.23.8"
   },
   "devDependencies": {
+    "@faker-js/faker": "^9.1.0",
+    "@testing-library/dom": "^10.4.0",
+    "@testing-library/react": "^16.0.1",
     "@types/node": "^20",
     "@types/react": "^18",
     "@types/react-dom": "^18",
+    "@vitejs/plugin-react": "^4.3.3",
     "bcrypt": "^5.1.1",
     "dotenv": "^16.4.5",
     "eslint": "^8",
     "eslint-config-next": "15.0.1",
+    "jsdom": "^25.0.1",
+    "moq.ts": "^10.0.8",
     "postcss": "^8",
     "tailwindcss": "^3.4.1",
-    "typescript": "^5"
+    "typescript": "^5",
+    "vitest": "^2.1.4"
   }
 }
diff --git a/src/app/lib/data.ts b/src/app/lib/data.ts
index c655f10..3afe7f1 100644
--- a/src/app/lib/data.ts
+++ b/src/app/lib/data.ts
@@ -1,10 +1,8 @@
 import { sql } from '@/bootstrap/db/db';
 import {
-  CustomerField,
   CustomersTableType,
   InvoiceForm,
   InvoicesTable,
-  User,
   Revenue,
   Invoice,
   Customer,
@@ -189,25 +187,6 @@ export async function fetchInvoiceById(id: string) {
   }
 }
 
-export async function fetchCustomers() {
-    // This is equivalent to in fetch(..., {cache: 'no-store'}).
-    connection()
-  try {
-    const data = await sql`
-      SELECT
-        id,
-        name
-      FROM customers
-      ORDER BY name ASC
-    ` as postgres.RowList<CustomerField[]>;
-
-    return data;
-  } catch (err) {
-    console.error('Database Error:', err);
-    throw new Error('Failed to fetch all customers.');
-  }
-}
-
 export async function fetchFilteredCustomers(query: string) {
     // This is equivalent to in fetch(..., {cache: 'no-store'}).
     connection()
@@ -242,13 +221,3 @@ export async function fetchFilteredCustomers(query: string) {
     throw new Error('Failed to fetch customer table.');
   }
 }
-
-export async function getUser(email: string) {
-  try {
-    const user = await sql`SELECT * FROM users WHERE email=${email}`;
-    return user.at(0) as User;
-  } catch (error) {
-    console.error('Failed to fetch user:', error);
-    throw new Error('Failed to fetch user.');
-  }
-}
diff --git a/src/app/lib/definitions.ts b/src/app/lib/definitions.ts
index e439b66..0860139 100644
--- a/src/app/lib/definitions.ts
+++ b/src/app/lib/definitions.ts
@@ -1,14 +1,3 @@
-// This file contains type definitions for your data.
-// It describes the shape of the data, and what data type each property should accept.
-// For simplicity of teaching, we're manually defining these types.
-// However, these types are generated automatically if you're using an ORM such as Prisma.
-export type User = {
-  id: string;
-  name: string;
-  email: string;
-  password: string;
-};
-
 export type Customer = {
   id: string;
   name: string;
@@ -73,11 +62,6 @@ export type FormattedCustomersTable = {
   total_paid: string;
 };
 
-export type CustomerField = {
-  id: string;
-  name: string;
-};
-
 export type InvoiceForm = {
   id: string;
   customer_id: string;
diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
index 54200bd..78095a9 100644
--- a/src/feature/common/server-di.ts
+++ b/src/feature/common/server-di.ts
@@ -1,10 +1,13 @@
+import { customerKey } from "@/feature/customer/customer-key";
+import getCustomerDi from "@/feature/customer/data/module/customer-di";
 import { testModuleKey } from "@/feature/domain/test/test-module-key";
 import getTestModule from "@/feature/infra/test/module/test-module";
 import { DependencyContainer } from "tsyringe";
 
 export default function serverDi(module: string): DependencyContainer {
     const getDi = {
-        [testModuleKey]: getTestModule
+        [testModuleKey]: getTestModule,
+        [customerKey]: getCustomerDi
     }[module]
 
     if (!getDi) throw new Error("Server Di didn't found for module: " + module)
diff --git a/src/feature/customer/customer-key.ts b/src/feature/customer/customer-key.ts
new file mode 100644
index 0000000..9ae5f45
--- /dev/null
+++ b/src/feature/customer/customer-key.ts
@@ -0,0 +1 @@
+export const customerKey = "customerKey"
\ No newline at end of file
diff --git a/src/feature/customer/data/module/customer-di.ts b/src/feature/customer/data/module/customer-di.ts
new file mode 100644
index 0000000..05df3ae
--- /dev/null
+++ b/src/feature/customer/data/module/customer-di.ts
@@ -0,0 +1,13 @@
+import di from "@/bootstrap/di/init-di";
+import fetchCustomersUsecase from "@/feature/customer/domain/usecase/fetch-customers-usecase";
+import { DependencyContainer } from "tsyringe";
+
+export default function getCustomerDi(): DependencyContainer {
+    const customerDi = di.createChildContainer()
+
+    customerDi.register(fetchCustomersUsecase.name, {
+        useValue: fetchCustomersUsecase
+    })
+
+    return customerDi
+}
\ No newline at end of file
diff --git a/src/feature/customer/domain/entity/customer.ts b/src/feature/customer/domain/entity/customer.ts
new file mode 100644
index 0000000..3b13a69
--- /dev/null
+++ b/src/feature/customer/domain/entity/customer.ts
@@ -0,0 +1,37 @@
+export type CustomersTableType = {
+  id: string;
+  name: string;
+  email: string;
+  image_url: string;
+  total_invoices: number;
+  total_pending: number;
+  total_paid: number;
+};
+
+export default class Customer {
+    id: string;
+    name: string;
+    email: string;
+    imageUrl: string;
+    totalInvoices: number;
+    totalPending: number;
+    totalPaid: number;
+
+    constructor({
+        id,
+        email,
+        imageUrl,
+        name,
+        totalInvoices,
+        totalPaid,
+        totalPending
+    }: Customer) {
+        this.id = id;
+        this.name = name;
+        this.email = email;
+        this.imageUrl = imageUrl;
+        this.totalInvoices = totalInvoices;
+        this.totalPaid = totalPaid;
+        this.totalPending = totalPending;
+    }
+}
\ No newline at end of file
diff --git a/src/feature/customer/domain/i-repo/customer-repo.ts b/src/feature/customer/domain/i-repo/customer-repo.ts
new file mode 100644
index 0000000..5c9cbf2
--- /dev/null
+++ b/src/feature/customer/domain/i-repo/customer-repo.ts
@@ -0,0 +1,7 @@
+import Customer from "@/feature/customer/domain/entity/customer"
+
+export default interface CustomerRepo {
+    fetchList(query: string): Promise<Customer[]>
+}
+
+export const customerRepoKey = "customerRepoKey"
\ No newline at end of file
diff --git a/src/feature/customer/domain/usecase/fetch-customers-usecase.ts b/src/feature/customer/domain/usecase/fetch-customers-usecase.ts
new file mode 100644
index 0000000..bf95239
--- /dev/null
+++ b/src/feature/customer/domain/usecase/fetch-customers-usecase.ts
@@ -0,0 +1,12 @@
+"use server"
+
+import serverDi from "@/feature/common/server-di";
+import { customerKey } from "@/feature/customer/customer-key";
+import Customer from "@/feature/customer/domain/entity/customer";
+import CustomerRepo, { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
+
+export default function fetchCustomersUsecase(query: string): Promise<Customer[]> {
+    const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
+
+    return repo.fetchList(query)
+}
\ No newline at end of file
diff --git a/src/test/common/fake-factory/customer/customer-fake-factory.ts b/src/test/common/fake-factory/customer/customer-fake-factory.ts
new file mode 100644
index 0000000..be69bec
--- /dev/null
+++ b/src/test/common/fake-factory/customer/customer-fake-factory.ts
@@ -0,0 +1,21 @@
+import Customer from "@/feature/customer/domain/entity/customer";
+import { faker } from "@faker-js/faker";
+
+export default class CustomerFakeFactory {
+    static getFakeCustomer(): Customer {
+        return new Customer({
+            id: faker.string.uuid(),
+            name: faker.person.fullName(),
+            email: faker.internet.email(),
+            imageUrl: faker.image.url(),
+            totalInvoices: faker.number.int(),
+            totalPaid: faker.number.int(),
+            totalPending: faker.number.int(),
+        })
+    }
+
+
+    static getFakeCustomerList(length: number = 10): Customer[] {
+        return Array.from({length}).map(() => CustomerFakeFactory.getFakeCustomer()) 
+    }
+}
\ No newline at end of file
diff --git a/src/test/common/mock/mock-di.ts b/src/test/common/mock/mock-di.ts
new file mode 100644
index 0000000..3bb7d4e
--- /dev/null
+++ b/src/test/common/mock/mock-di.ts
@@ -0,0 +1,7 @@
+import di from "@/bootstrap/di/init-di"
+import * as serverDi from "@/feature/common/server-di"
+
+export default function mockDi() {
+    vi.spyOn(serverDi, "default").mockReturnValue(di)
+    return di
+}
\ No newline at end of file
diff --git a/src/test/common/mock/mock-factory.ts b/src/test/common/mock/mock-factory.ts
new file mode 100644
index 0000000..8635c9e
--- /dev/null
+++ b/src/test/common/mock/mock-factory.ts
@@ -0,0 +1,5 @@
+import { Mock } from "moq.ts";
+
+export function getMock<T>() {
+  return new Mock<T>();
+}
diff --git a/src/test/setup.ts b/src/test/setup.ts
new file mode 100644
index 0000000..48d5e07
--- /dev/null
+++ b/src/test/setup.ts
@@ -0,0 +1 @@
+import "reflect-metadata";
\ No newline at end of file
diff --git a/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts b/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
new file mode 100644
index 0000000..7178ac2
--- /dev/null
+++ b/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
@@ -0,0 +1,48 @@
+import CustomerRepo, { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
+import { getMock } from "@/test/common/mock/mock-factory";
+import { describe } from "vitest";
+import { faker } from "@faker-js/faker";
+import CustomerFakeFactory from "@/test/common/fake-factory/customer/customer-fake-factory";
+import fetchCustomersUsecase from "@/feature/customer/domain/usecase/fetch-customers-usecase";
+import mockDi from "@/test/common/mock/mock-di";
+/* -------------------------------------------------------------------------- */
+/*                                   Faking                                   */
+/* -------------------------------------------------------------------------- */
+const fakedCustomerList = CustomerFakeFactory.getFakeCustomerList()
+/* -------------------------------------------------------------------------- */
+/*                                   Mocking                                  */
+/* -------------------------------------------------------------------------- */
+const customerDi = mockDi()
+
+const mockedFetchList = vi.fn<CustomerRepo['fetchList']>()
+const MockedRepo = getMock<CustomerRepo>()
+MockedRepo.setup((instance) => instance.fetchList).returns(mockedFetchList)
+/* -------------------------------------------------------------------------- */
+/*                                     DI                                     */
+/* -------------------------------------------------------------------------- */
+customerDi.register(fetchCustomersUsecase.name, {
+    useValue: fetchCustomersUsecase
+})
+customerDi.register(customerRepoKey, {
+    useValue: MockedRepo.object()
+})
+/* -------------------------------------------------------------------------- */
+/*                                   Testing                                  */
+/* -------------------------------------------------------------------------- */
+const usecase = customerDi.resolve<typeof fetchCustomersUsecase>(fetchCustomersUsecase.name)
+describe("Fetch customers", () => {
+    describe("On given query string", () => {
+        const fakedQuery = faker.person.fullName(); 
+        describe("And returning list from repo", () => {
+            beforeEach(() => {
+                mockedFetchList.mockResolvedValue(fakedCustomerList)
+            })
+            it("Then should return correct list of customers", async () => {
+              // ! Act
+              const response = await usecase(fakedQuery)
+              // ? Assert
+              expect(response).toEqual(fakedCustomerList)
+            })
+        });
+    });
+});
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index d42c189..ef29509 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -15,6 +15,7 @@
     "isolatedModules": true,
     "jsx": "preserve",
     "incremental": true,
+    "types": ["vitest/globals"],
     "plugins": [
       {
         "name": "next"
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..3676ec2
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,15 @@
+/// <reference types="vitest" />
+import { defineConfig } from "vitest/config";
+import path from 'path';
+
+export default defineConfig({
+    test: {
+        globals: true,
+        setupFiles: 'src/test/setup.ts',
+    },
+    resolve: {
+        alias: {
+            '@': path.resolve(__dirname, './src'),
+        },
+  },
+})
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 80c25e6..1a06bc5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7,6 +7,174 @@
   resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
   integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
 
+"@ampproject/remapping@^2.2.0":
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
+  integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.5"
+    "@jridgewell/trace-mapping" "^0.3.24"
+
+"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0":
+  version "7.26.2"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
+  integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.25.9"
+    js-tokens "^4.0.0"
+    picocolors "^1.0.0"
+
+"@babel/compat-data@^7.25.9":
+  version "7.26.2"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e"
+  integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==
+
+"@babel/core@^7.25.2":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40"
+  integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==
+  dependencies:
+    "@ampproject/remapping" "^2.2.0"
+    "@babel/code-frame" "^7.26.0"
+    "@babel/generator" "^7.26.0"
+    "@babel/helper-compilation-targets" "^7.25.9"
+    "@babel/helper-module-transforms" "^7.26.0"
+    "@babel/helpers" "^7.26.0"
+    "@babel/parser" "^7.26.0"
+    "@babel/template" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+    "@babel/types" "^7.26.0"
+    convert-source-map "^2.0.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.2"
+    json5 "^2.2.3"
+    semver "^6.3.1"
+
+"@babel/generator@^7.25.9", "@babel/generator@^7.26.0":
+  version "7.26.2"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f"
+  integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==
+  dependencies:
+    "@babel/parser" "^7.26.2"
+    "@babel/types" "^7.26.0"
+    "@jridgewell/gen-mapping" "^0.3.5"
+    "@jridgewell/trace-mapping" "^0.3.25"
+    jsesc "^3.0.2"
+
+"@babel/helper-compilation-targets@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875"
+  integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==
+  dependencies:
+    "@babel/compat-data" "^7.25.9"
+    "@babel/helper-validator-option" "^7.25.9"
+    browserslist "^4.24.0"
+    lru-cache "^5.1.1"
+    semver "^6.3.1"
+
+"@babel/helper-module-imports@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
+  integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
+  dependencies:
+    "@babel/traverse" "^7.25.9"
+    "@babel/types" "^7.25.9"
+
+"@babel/helper-module-transforms@^7.26.0":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
+  integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
+  dependencies:
+    "@babel/helper-module-imports" "^7.25.9"
+    "@babel/helper-validator-identifier" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+
+"@babel/helper-plugin-utils@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46"
+  integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==
+
+"@babel/helper-string-parser@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
+  integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
+
+"@babel/helper-validator-identifier@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
+  integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
+
+"@babel/helper-validator-option@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
+  integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==
+
+"@babel/helpers@^7.26.0":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4"
+  integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==
+  dependencies:
+    "@babel/template" "^7.25.9"
+    "@babel/types" "^7.26.0"
+
+"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2":
+  version "7.26.2"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11"
+  integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==
+  dependencies:
+    "@babel/types" "^7.26.0"
+
+"@babel/plugin-transform-react-jsx-self@^7.24.7":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz#c0b6cae9c1b73967f7f9eb2fca9536ba2fad2858"
+  integrity sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-react-jsx-source@^7.24.7":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz#4c6b8daa520b5f155b5fb55547d7c9fa91417503"
+  integrity sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/runtime@^7.12.5":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
+  integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
+  dependencies:
+    regenerator-runtime "^0.14.0"
+
+"@babel/template@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016"
+  integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==
+  dependencies:
+    "@babel/code-frame" "^7.25.9"
+    "@babel/parser" "^7.25.9"
+    "@babel/types" "^7.25.9"
+
+"@babel/traverse@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84"
+  integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==
+  dependencies:
+    "@babel/code-frame" "^7.25.9"
+    "@babel/generator" "^7.25.9"
+    "@babel/parser" "^7.25.9"
+    "@babel/template" "^7.25.9"
+    "@babel/types" "^7.25.9"
+    debug "^4.3.1"
+    globals "^11.1.0"
+
+"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff"
+  integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==
+  dependencies:
+    "@babel/helper-string-parser" "^7.25.9"
+    "@babel/helper-validator-identifier" "^7.25.9"
+
 "@emnapi/runtime@^1.2.0":
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60"
@@ -14,6 +182,121 @@
   dependencies:
     tslib "^2.4.0"
 
+"@esbuild/aix-ppc64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
+  integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
+
+"@esbuild/android-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
+  integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
+
+"@esbuild/android-arm@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
+  integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
+
+"@esbuild/android-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
+  integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
+
+"@esbuild/darwin-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
+  integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
+
+"@esbuild/darwin-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
+  integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
+
+"@esbuild/freebsd-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
+  integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
+
+"@esbuild/freebsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
+  integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
+
+"@esbuild/linux-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
+  integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
+
+"@esbuild/linux-arm@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
+  integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
+
+"@esbuild/linux-ia32@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
+  integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
+
+"@esbuild/linux-loong64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
+  integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
+
+"@esbuild/linux-mips64el@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
+  integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
+
+"@esbuild/linux-ppc64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
+  integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
+
+"@esbuild/linux-riscv64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
+  integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
+
+"@esbuild/linux-s390x@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
+  integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
+
+"@esbuild/linux-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
+  integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
+
+"@esbuild/netbsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
+  integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
+
+"@esbuild/openbsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
+  integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
+
+"@esbuild/sunos-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
+  integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
+
+"@esbuild/win32-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
+  integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
+
+"@esbuild/win32-ia32@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
+  integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
+
+"@esbuild/win32-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
+  integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
+
 "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
   version "4.4.1"
   resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56"
@@ -46,6 +329,11 @@
   resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
   integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
 
+"@faker-js/faker@^9.1.0":
+  version "9.1.0"
+  resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.1.0.tgz#5d7957df87e2fb0eee5dcfd311ba83b34ec8eead"
+  integrity sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug==
+
 "@heroicons/react@^2.1.5":
   version "2.1.5"
   resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.5.tgz#1e13f34976cc542deae92353c01c8b3d7942e9ba"
@@ -195,7 +483,7 @@
     wrap-ansi "^8.1.0"
     wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
 
-"@jridgewell/gen-mapping@^0.3.2":
+"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5":
   version "0.3.5"
   resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
   integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
@@ -214,12 +502,12 @@
   resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
   integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
 
-"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
   integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
 
-"@jridgewell/trace-mapping@^0.3.24":
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
   version "0.3.25"
   resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
   integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
@@ -330,6 +618,96 @@
   resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.1.tgz#462c85fd726a77854cd5956e29eb19a575aa7ce5"
   integrity sha512-QvYompk0X+8Yjlo/Fv4McrzxohDdM5GgLHyQcPpcsPvlOSXCGFjdbuyGL5dzRbg0GpknAjQJJZzdiRK7iWVuFQ==
 
+"@rollup/rollup-android-arm-eabi@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz#49a2a9808074f2683667992aa94b288e0b54fc82"
+  integrity sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==
+
+"@rollup/rollup-android-arm64@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz#197e3bc01c228d3c23591e0fcedca91f8f398ec1"
+  integrity sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==
+
+"@rollup/rollup-darwin-arm64@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz#16772c0309d0dc3cca716580cdac7a1c560ddf46"
+  integrity sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==
+
+"@rollup/rollup-darwin-x64@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz#4e98120a1c4cda7d4043ccce72347cee53784140"
+  integrity sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==
+
+"@rollup/rollup-freebsd-arm64@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz#27145e414986e216e0d9b9a8d488028f33c39566"
+  integrity sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==
+
+"@rollup/rollup-freebsd-x64@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz#67e75fd87a903090f038b212273c492e5ca6b32f"
+  integrity sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==
+
+"@rollup/rollup-linux-arm-gnueabihf@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz#bb45ebadbb9496298ab5461373bde357e8f33e88"
+  integrity sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==
+
+"@rollup/rollup-linux-arm-musleabihf@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz#384276c23feb0a4d6ffa603a9a760decce8b4118"
+  integrity sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==
+
+"@rollup/rollup-linux-arm64-gnu@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz#89e5a4570ddd9eca908324a6de60bd64f904e3f0"
+  integrity sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==
+
+"@rollup/rollup-linux-arm64-musl@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz#9ffd7cd6c6c6670d8c039056d6a49ad9f1f66949"
+  integrity sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==
+
+"@rollup/rollup-linux-powerpc64le-gnu@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz#4d32ce982e2d25e3b8116336ad5ce6e270b5a024"
+  integrity sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==
+
+"@rollup/rollup-linux-riscv64-gnu@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz#f43d4e0572397e3d3acd82d77d79ce021dea3310"
+  integrity sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==
+
+"@rollup/rollup-linux-s390x-gnu@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz#264f8a4c206173945bdab2a676d638b7945106a9"
+  integrity sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==
+
+"@rollup/rollup-linux-x64-gnu@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz#e86172a407b2edd41540ec2ae636e497fadccff6"
+  integrity sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==
+
+"@rollup/rollup-linux-x64-musl@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz#8ae9bf78986d1b16ccbc89ab6f2dfa96807d3178"
+  integrity sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==
+
+"@rollup/rollup-win32-arm64-msvc@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz#11d6a59f651a3c2a9e5eaab0a99367b77a29c319"
+  integrity sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==
+
+"@rollup/rollup-win32-ia32-msvc@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz#7ff146e53dc6e388b60329b7ec3335501d2b0f98"
+  integrity sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==
+
+"@rollup/rollup-win32-x64-msvc@4.24.3":
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz#7687335781efe6bee14d6ed8eff9746a9f24c9cd"
+  integrity sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==
+
 "@rtsao/scc@^1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
@@ -352,6 +730,70 @@
   dependencies:
     tslib "^2.4.0"
 
+"@testing-library/dom@^10.4.0":
+  version "10.4.0"
+  resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8"
+  integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==
+  dependencies:
+    "@babel/code-frame" "^7.10.4"
+    "@babel/runtime" "^7.12.5"
+    "@types/aria-query" "^5.0.1"
+    aria-query "5.3.0"
+    chalk "^4.1.0"
+    dom-accessibility-api "^0.5.9"
+    lz-string "^1.5.0"
+    pretty-format "^27.0.2"
+
+"@testing-library/react@^16.0.1":
+  version "16.0.1"
+  resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.0.1.tgz#29c0ee878d672703f5e7579f239005e4e0faa875"
+  integrity sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==
+  dependencies:
+    "@babel/runtime" "^7.12.5"
+
+"@types/aria-query@^5.0.1":
+  version "5.0.4"
+  resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708"
+  integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
+
+"@types/babel__core@^7.20.5":
+  version "7.20.5"
+  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
+  integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
+  dependencies:
+    "@babel/parser" "^7.20.7"
+    "@babel/types" "^7.20.7"
+    "@types/babel__generator" "*"
+    "@types/babel__template" "*"
+    "@types/babel__traverse" "*"
+
+"@types/babel__generator@*":
+  version "7.6.8"
+  resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab"
+  integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
+"@types/babel__template@*":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f"
+  integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==
+  dependencies:
+    "@babel/parser" "^7.1.0"
+    "@babel/types" "^7.0.0"
+
+"@types/babel__traverse@*":
+  version "7.20.6"
+  resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7"
+  integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==
+  dependencies:
+    "@babel/types" "^7.20.7"
+
+"@types/estree@1.0.6", "@types/estree@^1.0.0":
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
+  integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
+
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -470,6 +912,76 @@
   resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
   integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
 
+"@vitejs/plugin-react@^4.3.3":
+  version "4.3.3"
+  resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz#28301ac6d7aaf20b73a418ee5c65b05519b4836c"
+  integrity sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==
+  dependencies:
+    "@babel/core" "^7.25.2"
+    "@babel/plugin-transform-react-jsx-self" "^7.24.7"
+    "@babel/plugin-transform-react-jsx-source" "^7.24.7"
+    "@types/babel__core" "^7.20.5"
+    react-refresh "^0.14.2"
+
+"@vitest/expect@2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.4.tgz#48f4f53a01092a3bdc118cff245f79ef388bdd8e"
+  integrity sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==
+  dependencies:
+    "@vitest/spy" "2.1.4"
+    "@vitest/utils" "2.1.4"
+    chai "^5.1.2"
+    tinyrainbow "^1.2.0"
+
+"@vitest/mocker@2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.4.tgz#0dc07edb9114f7f080a0181fbcdb16cd4a2d855d"
+  integrity sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==
+  dependencies:
+    "@vitest/spy" "2.1.4"
+    estree-walker "^3.0.3"
+    magic-string "^0.30.12"
+
+"@vitest/pretty-format@2.1.4", "@vitest/pretty-format@^2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.4.tgz#fc31993bdc1ef5a6c1a4aa6844e7ba55658a4f9f"
+  integrity sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==
+  dependencies:
+    tinyrainbow "^1.2.0"
+
+"@vitest/runner@2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.4.tgz#f9346500bdd0be1c926daaac5d683bae87ceda2c"
+  integrity sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==
+  dependencies:
+    "@vitest/utils" "2.1.4"
+    pathe "^1.1.2"
+
+"@vitest/snapshot@2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.4.tgz#ef8c3f605fbc23a32773256d37d3fdfd9b23d353"
+  integrity sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==
+  dependencies:
+    "@vitest/pretty-format" "2.1.4"
+    magic-string "^0.30.12"
+    pathe "^1.1.2"
+
+"@vitest/spy@2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.4.tgz#4e90f9783437c5841a27c80f8fd84d7289a6100a"
+  integrity sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==
+  dependencies:
+    tinyspy "^3.0.2"
+
+"@vitest/utils@2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.4.tgz#6d67ac966647a21ce8bc497472ce230de3b64537"
+  integrity sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==
+  dependencies:
+    "@vitest/pretty-format" "2.1.4"
+    loupe "^3.1.2"
+    tinyrainbow "^1.2.0"
+
 abbrev@1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -492,6 +1004,13 @@ agent-base@6:
   dependencies:
     debug "4"
 
+agent-base@^7.0.2, agent-base@^7.1.0:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317"
+  integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==
+  dependencies:
+    debug "^4.3.4"
+
 ajv@^6.12.4:
   version "6.12.6"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -519,6 +1038,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
   dependencies:
     color-convert "^2.0.1"
 
+ansi-styles@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
+  integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
+
 ansi-styles@^6.1.0:
   version "6.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
@@ -560,6 +1084,13 @@ argparse@^2.0.1:
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
   integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
 
+aria-query@5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
+  integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==
+  dependencies:
+    dequal "^2.0.3"
+
 aria-query@^5.3.2:
   version "5.3.2"
   resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59"
@@ -654,11 +1185,21 @@ arraybuffer.prototype.slice@^1.0.3:
     is-array-buffer "^3.0.4"
     is-shared-array-buffer "^1.0.2"
 
+assertion-error@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
+  integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
+
 ast-types-flow@^0.0.8:
   version "0.0.8"
   resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6"
   integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==
 
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
 available-typed-arrays@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
@@ -716,6 +1257,16 @@ braces@^3.0.3, braces@~3.0.2:
   dependencies:
     fill-range "^7.1.1"
 
+browserslist@^4.24.0:
+  version "4.24.2"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580"
+  integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==
+  dependencies:
+    caniuse-lite "^1.0.30001669"
+    electron-to-chromium "^1.5.41"
+    node-releases "^2.0.18"
+    update-browserslist-db "^1.1.1"
+
 busboy@1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
@@ -723,6 +1274,11 @@ busboy@1.6.0:
   dependencies:
     streamsearch "^1.1.0"
 
+cac@^6.7.14:
+  version "6.7.14"
+  resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
+  integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
+
 call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
@@ -744,12 +1300,23 @@ camelcase-css@^2.0.1:
   resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
   integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
 
-caniuse-lite@^1.0.30001579:
+caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001669:
   version "1.0.30001676"
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz#fe133d41fe74af8f7cc93b8a714c3e86a86e6f04"
   integrity sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==
 
-chalk@^4.0.0:
+chai@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d"
+  integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==
+  dependencies:
+    assertion-error "^2.0.1"
+    check-error "^2.1.1"
+    deep-eql "^5.0.1"
+    loupe "^3.1.0"
+    pathval "^2.0.0"
+
+chalk@^4.0.0, chalk@^4.1.0:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
   integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -757,6 +1324,11 @@ chalk@^4.0.0:
     ansi-styles "^4.1.0"
     supports-color "^7.1.0"
 
+check-error@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
+  integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
+
 chokidar@^3.5.3:
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
@@ -832,6 +1404,13 @@ color@^4.2.3:
     color-convert "^2.0.1"
     color-string "^1.9.0"
 
+combined-stream@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
 commander@^4.0.0:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
@@ -847,6 +1426,11 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0:
   resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
   integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
 
+convert-source-map@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
+  integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+
 cross-spawn@^7.0.0, cross-spawn@^7.0.2:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -861,6 +1445,13 @@ cssesc@^3.0.0:
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
   integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
 
+cssstyle@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.1.0.tgz#161faee382af1bafadb6d3867a92a19bcb4aea70"
+  integrity sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==
+  dependencies:
+    rrweb-cssom "^0.7.1"
+
 csstype@^3.0.2:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
@@ -871,6 +1462,14 @@ damerau-levenshtein@^1.0.8:
   resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
   integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
 
+data-urls@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde"
+  integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==
+  dependencies:
+    whatwg-mimetype "^4.0.0"
+    whatwg-url "^14.0.0"
+
 data-view-buffer@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2"
@@ -898,7 +1497,7 @@ data-view-byte-offset@^1.0.0:
     es-errors "^1.3.0"
     is-data-view "^1.0.1"
 
-debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5:
+debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7:
   version "4.3.7"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
   integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
@@ -912,6 +1511,16 @@ debug@^3.2.7:
   dependencies:
     ms "^2.1.1"
 
+decimal.js@^10.4.3:
+  version "10.4.3"
+  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
+  integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
+
+deep-eql@^5.0.1:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
+  integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
+
 deep-is@^0.1.3:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
@@ -935,11 +1544,21 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
     has-property-descriptors "^1.0.0"
     object-keys "^1.1.1"
 
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
 delegates@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
   integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
 
+dequal@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
+  integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
+
 detect-libc@^2.0.0, detect-libc@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
@@ -969,6 +1588,11 @@ doctrine@^3.0.0:
   dependencies:
     esutils "^2.0.2"
 
+dom-accessibility-api@^0.5.9:
+  version "0.5.16"
+  resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
+  integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
+
 dotenv@^16.4.5:
   version "16.4.5"
   resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
@@ -979,6 +1603,11 @@ eastasianwidth@^0.2.0:
   resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
   integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
 
+electron-to-chromium@^1.5.41:
+  version "1.5.50"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz#d9ba818da7b2b5ef1f3dd32bce7046feb7e93234"
+  integrity sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==
+
 emoji-regex@^8.0.0:
   version "8.0.0"
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@@ -997,6 +1626,11 @@ enhanced-resolve@^5.15.0:
     graceful-fs "^4.2.4"
     tapable "^2.2.0"
 
+entities@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
+  integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
 es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3:
   version "1.23.3"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0"
@@ -1113,6 +1747,40 @@ es-to-primitive@^1.2.1:
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
+esbuild@^0.21.3:
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
+  integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
+  optionalDependencies:
+    "@esbuild/aix-ppc64" "0.21.5"
+    "@esbuild/android-arm" "0.21.5"
+    "@esbuild/android-arm64" "0.21.5"
+    "@esbuild/android-x64" "0.21.5"
+    "@esbuild/darwin-arm64" "0.21.5"
+    "@esbuild/darwin-x64" "0.21.5"
+    "@esbuild/freebsd-arm64" "0.21.5"
+    "@esbuild/freebsd-x64" "0.21.5"
+    "@esbuild/linux-arm" "0.21.5"
+    "@esbuild/linux-arm64" "0.21.5"
+    "@esbuild/linux-ia32" "0.21.5"
+    "@esbuild/linux-loong64" "0.21.5"
+    "@esbuild/linux-mips64el" "0.21.5"
+    "@esbuild/linux-ppc64" "0.21.5"
+    "@esbuild/linux-riscv64" "0.21.5"
+    "@esbuild/linux-s390x" "0.21.5"
+    "@esbuild/linux-x64" "0.21.5"
+    "@esbuild/netbsd-x64" "0.21.5"
+    "@esbuild/openbsd-x64" "0.21.5"
+    "@esbuild/sunos-x64" "0.21.5"
+    "@esbuild/win32-arm64" "0.21.5"
+    "@esbuild/win32-ia32" "0.21.5"
+    "@esbuild/win32-x64" "0.21.5"
+
+escalade@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+  integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+
 escape-string-regexp@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
@@ -1324,11 +1992,23 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
   integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
 
+estree-walker@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
+  integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
+  dependencies:
+    "@types/estree" "^1.0.0"
+
 esutils@^2.0.2:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
   integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
 
+expect-type@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75"
+  integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==
+
 fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -1424,6 +2104,15 @@ foreground-child@^3.1.0:
     cross-spawn "^7.0.0"
     signal-exit "^4.0.1"
 
+form-data@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48"
+  integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    mime-types "^2.1.12"
+
 fs-minipass@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@@ -1436,7 +2125,7 @@ fs.realpath@^1.0.0:
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
   integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
 
-fsevents@~2.3.2:
+fsevents@~2.3.2, fsevents@~2.3.3:
   version "2.3.3"
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
   integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
@@ -1476,6 +2165,11 @@ gauge@^3.0.0:
     strip-ansi "^6.0.1"
     wide-align "^1.1.2"
 
+gensync@^1.0.0-beta.2:
+  version "1.0.0-beta.2"
+  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+  integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
 get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
@@ -1541,6 +2235,11 @@ glob@^7.1.3:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
+globals@^11.1.0:
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
 globals@^13.19.0:
   version "13.24.0"
   resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
@@ -1619,6 +2318,21 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
   dependencies:
     function-bind "^1.1.2"
 
+html-encoding-sniffer@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448"
+  integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==
+  dependencies:
+    whatwg-encoding "^3.1.1"
+
+http-proxy-agent@^7.0.2:
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
+  integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==
+  dependencies:
+    agent-base "^7.1.0"
+    debug "^4.3.4"
+
 https-proxy-agent@^5.0.0:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
@@ -1627,6 +2341,21 @@ https-proxy-agent@^5.0.0:
     agent-base "6"
     debug "4"
 
+https-proxy-agent@^7.0.5:
+  version "7.0.5"
+  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2"
+  integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==
+  dependencies:
+    agent-base "^7.0.2"
+    debug "4"
+
+iconv-lite@0.6.3:
+  version "0.6.3"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+  integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3.0.0"
+
 ignore@^5.2.0, ignore@^5.3.1:
   version "5.3.2"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
@@ -1800,6 +2529,11 @@ is-path-inside@^3.0.3:
   resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
   integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
 
+is-potential-custom-element-name@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
+  integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
+
 is-regex@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
@@ -1896,7 +2630,7 @@ jiti@^1.21.0:
   resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268"
   integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
 
-"js-tokens@^3.0.0 || ^4.0.0":
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
   integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
@@ -1908,6 +2642,38 @@ js-yaml@^4.1.0:
   dependencies:
     argparse "^2.0.1"
 
+jsdom@^25.0.1:
+  version "25.0.1"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-25.0.1.tgz#536ec685c288fc8a5773a65f82d8b44badcc73ef"
+  integrity sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==
+  dependencies:
+    cssstyle "^4.1.0"
+    data-urls "^5.0.0"
+    decimal.js "^10.4.3"
+    form-data "^4.0.0"
+    html-encoding-sniffer "^4.0.0"
+    http-proxy-agent "^7.0.2"
+    https-proxy-agent "^7.0.5"
+    is-potential-custom-element-name "^1.0.1"
+    nwsapi "^2.2.12"
+    parse5 "^7.1.2"
+    rrweb-cssom "^0.7.1"
+    saxes "^6.0.0"
+    symbol-tree "^3.2.4"
+    tough-cookie "^5.0.0"
+    w3c-xmlserializer "^5.0.0"
+    webidl-conversions "^7.0.0"
+    whatwg-encoding "^3.1.1"
+    whatwg-mimetype "^4.0.0"
+    whatwg-url "^14.0.0"
+    ws "^8.18.0"
+    xml-name-validator "^5.0.0"
+
+jsesc@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e"
+  integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==
+
 json-buffer@3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
@@ -1930,6 +2696,11 @@ json5@^1.0.2:
   dependencies:
     minimist "^1.2.0"
 
+json5@^2.2.3:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
 "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5:
   version "3.3.5"
   resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
@@ -2001,16 +2772,40 @@ loose-envify@^1.4.0:
   dependencies:
     js-tokens "^3.0.0 || ^4.0.0"
 
+loupe@^3.1.0, loupe@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240"
+  integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==
+
 lru-cache@^10.2.0:
   version "10.4.3"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
   integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
 
+lru-cache@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+  integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+  dependencies:
+    yallist "^3.0.2"
+
 lucide-react@^0.454.0:
   version "0.454.0"
   resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.454.0.tgz#a81b9c482018720f07ead0503ae502d94d528444"
   integrity sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==
 
+lz-string@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
+  integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
+
+magic-string@^0.30.12:
+  version "0.30.12"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.12.tgz#9eb11c9d072b9bcb4940a5b2c2e1a217e4ee1a60"
+  integrity sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+
 make-dir@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@@ -2031,6 +2826,18 @@ micromatch@^4.0.4, micromatch@^4.0.5:
     braces "^3.0.3"
     picomatch "^2.3.1"
 
+mime-db@1.52.0:
+  version "1.52.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12:
+  version "2.1.35"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+  dependencies:
+    mime-db "1.52.0"
+
 minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -2080,6 +2887,13 @@ mkdirp@^1.0.3:
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
 
+moq.ts@^10.0.8:
+  version "10.0.8"
+  resolved "https://registry.yarnpkg.com/moq.ts/-/moq.ts-10.0.8.tgz#844f317c09a3450469e4fb3cc95252a6ed631581"
+  integrity sha512-9zAJcsqhVCNczXz8xfDHSjU0SYq6umAFRB1CDRpgE1vwK7EMqc+DAUV6vUS+Ttl8CAQFFtTiWB46g3axdcrzmQ==
+  dependencies:
+    tslib "*"
+
 ms@^2.1.1, ms@^2.1.3:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -2139,6 +2953,11 @@ node-fetch@^2.6.7:
   dependencies:
     whatwg-url "^5.0.0"
 
+node-releases@^2.0.18:
+  version "2.0.18"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
+  integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
+
 nopt@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
@@ -2161,6 +2980,11 @@ npmlog@^5.0.1:
     gauge "^3.0.0"
     set-blocking "^2.0.0"
 
+nwsapi@^2.2.12:
+  version "2.2.13"
+  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.13.tgz#e56b4e98960e7a040e5474536587e599c4ff4655"
+  integrity sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==
+
 object-assign@^4.0.1, object-assign@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -2273,6 +3097,13 @@ parent-module@^1.0.0:
   dependencies:
     callsites "^3.0.0"
 
+parse5@^7.1.2:
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a"
+  integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==
+  dependencies:
+    entities "^4.5.0"
+
 path-exists@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@@ -2301,6 +3132,16 @@ path-scurry@^1.11.1:
     lru-cache "^10.2.0"
     minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
 
+pathe@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
+  integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
+
+pathval@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25"
+  integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==
+
 picocolors@^1.0.0, picocolors@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
@@ -2379,7 +3220,7 @@ postcss@8.4.31:
     picocolors "^1.0.0"
     source-map-js "^1.0.2"
 
-postcss@^8, postcss@^8.4.23:
+postcss@^8, postcss@^8.4.23, postcss@^8.4.43:
   version "8.4.47"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
   integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
@@ -2398,6 +3239,15 @@ prelude-ls@^1.2.1:
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
   integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
 
+pretty-format@^27.0.2:
+  version "27.5.1"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
+  integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
+  dependencies:
+    ansi-regex "^5.0.1"
+    ansi-styles "^5.0.0"
+    react-is "^17.0.1"
+
 prop-types@^15.8.1:
   version "15.8.1"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
@@ -2407,7 +3257,7 @@ prop-types@^15.8.1:
     object-assign "^4.1.1"
     react-is "^16.13.1"
 
-punycode@^2.1.0:
+punycode@^2.1.0, punycode@^2.3.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
   integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
@@ -2429,6 +3279,16 @@ react-is@^16.13.1:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 
+react-is@^17.0.1:
+  version "17.0.2"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
+  integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+
+react-refresh@^0.14.2:
+  version "0.14.2"
+  resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"
+  integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==
+
 react@19.0.0-rc-69d4b800-20241021:
   version "19.0.0-rc-69d4b800-20241021"
   resolved "https://registry.yarnpkg.com/react/-/react-19.0.0-rc-69d4b800-20241021.tgz#2c3ce2a581df8c4c9e4059189af29b89022edd41"
@@ -2475,6 +3335,11 @@ reflect.getprototypeof@^1.0.4:
     globalthis "^1.0.3"
     which-builtin-type "^1.1.3"
 
+regenerator-runtime@^0.14.0:
+  version "0.14.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
+  integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
+
 regexp.prototype.flags@^1.5.2:
   version "1.5.3"
   resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42"
@@ -2525,6 +3390,38 @@ rimraf@^3.0.2:
   dependencies:
     glob "^7.1.3"
 
+rollup@^4.20.0:
+  version "4.24.3"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.24.3.tgz#8b259063740af60b0030315f88665ba2041789b8"
+  integrity sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==
+  dependencies:
+    "@types/estree" "1.0.6"
+  optionalDependencies:
+    "@rollup/rollup-android-arm-eabi" "4.24.3"
+    "@rollup/rollup-android-arm64" "4.24.3"
+    "@rollup/rollup-darwin-arm64" "4.24.3"
+    "@rollup/rollup-darwin-x64" "4.24.3"
+    "@rollup/rollup-freebsd-arm64" "4.24.3"
+    "@rollup/rollup-freebsd-x64" "4.24.3"
+    "@rollup/rollup-linux-arm-gnueabihf" "4.24.3"
+    "@rollup/rollup-linux-arm-musleabihf" "4.24.3"
+    "@rollup/rollup-linux-arm64-gnu" "4.24.3"
+    "@rollup/rollup-linux-arm64-musl" "4.24.3"
+    "@rollup/rollup-linux-powerpc64le-gnu" "4.24.3"
+    "@rollup/rollup-linux-riscv64-gnu" "4.24.3"
+    "@rollup/rollup-linux-s390x-gnu" "4.24.3"
+    "@rollup/rollup-linux-x64-gnu" "4.24.3"
+    "@rollup/rollup-linux-x64-musl" "4.24.3"
+    "@rollup/rollup-win32-arm64-msvc" "4.24.3"
+    "@rollup/rollup-win32-ia32-msvc" "4.24.3"
+    "@rollup/rollup-win32-x64-msvc" "4.24.3"
+    fsevents "~2.3.2"
+
+rrweb-cssom@^0.7.1:
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b"
+  integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==
+
 run-parallel@^1.1.9:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
@@ -2556,6 +3453,18 @@ safe-regex-test@^1.0.3:
     es-errors "^1.3.0"
     is-regex "^1.1.4"
 
+"safer-buffer@>= 2.1.2 < 3.0.0":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+saxes@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
+  integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==
+  dependencies:
+    xmlchars "^2.2.0"
+
 scheduler@0.25.0-rc-69d4b800-20241021:
   version "0.25.0-rc-69d4b800-20241021"
   resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-69d4b800-20241021.tgz#336e47ef2bd5eddb0ebacfd910b5df1b236d92bd"
@@ -2649,6 +3558,11 @@ side-channel@^1.0.4, side-channel@^1.0.6:
     get-intrinsic "^1.2.4"
     object-inspect "^1.13.1"
 
+siginfo@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
+  integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
+
 signal-exit@^3.0.0:
   version "3.0.7"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
@@ -2671,6 +3585,16 @@ source-map-js@^1.0.2, source-map-js@^1.2.1:
   resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
   integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
 
+stackback@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
+  integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
+
+std-env@^3.7.0:
+  version "3.7.0"
+  resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2"
+  integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==
+
 streamsearch@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
@@ -2836,6 +3760,11 @@ supports-preserve-symlinks-flag@^1.0.0:
   resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
   integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
 
+symbol-tree@^3.2.4:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
+  integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+
 tailwind-merge@^2.5.4:
   version "2.5.4"
   resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.5.4.tgz#4bf574e81fa061adeceba099ae4df56edcee78d1"
@@ -2910,6 +3839,43 @@ thenify-all@^1.0.0:
   dependencies:
     any-promise "^1.0.0"
 
+tinybench@^2.9.0:
+  version "2.9.0"
+  resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
+  integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
+
+tinyexec@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
+  integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
+
+tinypool@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.1.tgz#c64233c4fac4304e109a64340178760116dbe1fe"
+  integrity sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==
+
+tinyrainbow@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5"
+  integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==
+
+tinyspy@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
+  integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
+
+tldts-core@^6.1.57:
+  version "6.1.57"
+  resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.57.tgz#2cc6e6af3d0807ad616900300083930efa81b57d"
+  integrity sha512-lXnRhuQpx3zU9EONF9F7HfcRLvN1uRYUBIiKL+C/gehC/77XTU+Jye6ui86GA3rU6FjlJ0triD1Tkjt2F/2lEg==
+
+tldts@^6.1.32:
+  version "6.1.57"
+  resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.57.tgz#5d91d257ac945837358fe3954343fc01122fbad7"
+  integrity sha512-Oy7yDXK8meJl8vPMOldzA+MtueAJ5BrH4l4HXwZuj2AtfoQbLjmTJmjNWPUcAo+E/ibHn7QlqMS0BOcXJFJyHQ==
+  dependencies:
+    tldts-core "^6.1.57"
+
 to-regex-range@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@@ -2917,6 +3883,20 @@ to-regex-range@^5.0.1:
   dependencies:
     is-number "^7.0.0"
 
+tough-cookie@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.0.0.tgz#6b6518e2b5c070cf742d872ee0f4f92d69eac1af"
+  integrity sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==
+  dependencies:
+    tldts "^6.1.32"
+
+tr46@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec"
+  integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==
+  dependencies:
+    punycode "^2.3.1"
+
 tr46@~0.0.3:
   version "0.0.3"
   resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@@ -2942,16 +3922,16 @@ tsconfig-paths@^3.15.0:
     minimist "^1.2.6"
     strip-bom "^3.0.0"
 
+tslib@*, tslib@^2.4.0:
+  version "2.8.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
 tslib@^1.9.3:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
-tslib@^2.4.0:
-  version "2.8.1"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
-  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
-
 tsyringe@^4.8.0:
   version "4.8.0"
   resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.8.0.tgz#d599651b36793ba872870fee4f845bd484a5cac1"
@@ -3035,6 +4015,14 @@ undici-types@~6.19.2:
   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
   integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
 
+update-browserslist-db@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
+  integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==
+  dependencies:
+    escalade "^3.2.0"
+    picocolors "^1.1.0"
+
 uri-js@^4.2.2:
   version "4.4.1"
   resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
@@ -3047,11 +4035,90 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2:
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
   integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
 
+vite-node@2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.4.tgz#97ffb6de913fd8d42253afe441f9512e9dbdfd5c"
+  integrity sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==
+  dependencies:
+    cac "^6.7.14"
+    debug "^4.3.7"
+    pathe "^1.1.2"
+    vite "^5.0.0"
+
+vite@^5.0.0:
+  version "5.4.10"
+  resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.10.tgz#d358a7bd8beda6cf0f3b7a450a8c7693a4f80c18"
+  integrity sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==
+  dependencies:
+    esbuild "^0.21.3"
+    postcss "^8.4.43"
+    rollup "^4.20.0"
+  optionalDependencies:
+    fsevents "~2.3.3"
+
+vitest@^2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.4.tgz#ba8f4589fb639cf5a9e6af54781667312b3e8230"
+  integrity sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==
+  dependencies:
+    "@vitest/expect" "2.1.4"
+    "@vitest/mocker" "2.1.4"
+    "@vitest/pretty-format" "^2.1.4"
+    "@vitest/runner" "2.1.4"
+    "@vitest/snapshot" "2.1.4"
+    "@vitest/spy" "2.1.4"
+    "@vitest/utils" "2.1.4"
+    chai "^5.1.2"
+    debug "^4.3.7"
+    expect-type "^1.1.0"
+    magic-string "^0.30.12"
+    pathe "^1.1.2"
+    std-env "^3.7.0"
+    tinybench "^2.9.0"
+    tinyexec "^0.3.1"
+    tinypool "^1.0.1"
+    tinyrainbow "^1.2.0"
+    vite "^5.0.0"
+    vite-node "2.1.4"
+    why-is-node-running "^2.3.0"
+
+w3c-xmlserializer@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c"
+  integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==
+  dependencies:
+    xml-name-validator "^5.0.0"
+
 webidl-conversions@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
   integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
 
+webidl-conversions@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
+  integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
+
+whatwg-encoding@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5"
+  integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==
+  dependencies:
+    iconv-lite "0.6.3"
+
+whatwg-mimetype@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a"
+  integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==
+
+whatwg-url@^14.0.0:
+  version "14.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6"
+  integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==
+  dependencies:
+    tr46 "^5.0.0"
+    webidl-conversions "^7.0.0"
+
 whatwg-url@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
@@ -3117,6 +4184,14 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
+why-is-node-running@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
+  integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
+  dependencies:
+    siginfo "^2.0.0"
+    stackback "0.0.2"
+
 wide-align@^1.1.2:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
@@ -3152,6 +4227,26 @@ wrappy@1:
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
+ws@^8.18.0:
+  version "8.18.0"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
+  integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
+
+xml-name-validator@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673"
+  integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==
+
+xmlchars@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
+  integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
+
+yallist@^3.0.2:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
 yallist@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
-- 
2.39.5


From 46bbb9f8da607083979584592004d18e2b0e2e66 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 15:34:04 +0300
Subject: [PATCH 08/37] Add fetch list of customer invoice

---
 src/app/lib/data.ts                           | 129 +-----------------
 src/app/lib/definitions.ts                    |  36 -----
 .../data/module/customer-di.ts                |  16 +++
 .../data/repo/customer-invoice-db-repo.ts     |  51 +++++++
 .../domain/entity/customer-invoice.ts         |  21 +++
 .../domain/i-repo/customer-invoice-repo.ts    |   7 +
 .../fetch-customer-invoices-usecase.ts        |  11 ++
 .../customer-invoice/invoice-module-key.ts    |   1 +
 .../customer/data/module/customer-di.ts       |   3 +
 .../customer/data/repo/customer-db-repo.ts    |  66 +++++++++
 .../customer/domain/entity/customer.ts        |  16 +--
 .../customer/customer-fake-factory.ts         |   6 +-
 12 files changed, 183 insertions(+), 180 deletions(-)
 create mode 100644 src/feature/customer-invoice/data/module/customer-di.ts
 create mode 100644 src/feature/customer-invoice/data/repo/customer-invoice-db-repo.ts
 create mode 100644 src/feature/customer-invoice/domain/entity/customer-invoice.ts
 create mode 100644 src/feature/customer-invoice/domain/i-repo/customer-invoice-repo.ts
 create mode 100644 src/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
 create mode 100644 src/feature/customer-invoice/invoice-module-key.ts
 create mode 100644 src/feature/customer/data/repo/customer-db-repo.ts

diff --git a/src/app/lib/data.ts b/src/app/lib/data.ts
index 3afe7f1..d76cf24 100644
--- a/src/app/lib/data.ts
+++ b/src/app/lib/data.ts
@@ -1,8 +1,5 @@
 import { sql } from '@/bootstrap/db/db';
 import {
-  CustomersTableType,
-  InvoiceForm,
-  InvoicesTable,
   Revenue,
   Invoice,
   Customer,
@@ -59,7 +56,7 @@ export async function fetchLatestInvoices() {
 
 export async function fetchCardData() {
     // This is equivalent to in fetch(..., {cache: 'no-store'}).
-    connection()
+  connection()
 
   try {
     // You can probably combine these into a single SQL query
@@ -97,127 +94,3 @@ export async function fetchCardData() {
     throw new Error('Failed to fetch card data.');
   }
 }
-
-const ITEMS_PER_PAGE = 6;
-export async function fetchFilteredInvoices(
-  query: string,
-  currentPage: number,
-) {
-    // This is equivalent to in fetch(..., {cache: 'no-store'}).
-  connection()
-
-  const offset = (currentPage - 1) * ITEMS_PER_PAGE;
-
-  try {
-    const invoices = await sql`
-      SELECT
-        invoices.id,
-        invoices.amount,
-        invoices.date,
-        invoices.status,
-        customers.name,
-        customers.email,
-        customers.image_url
-      FROM invoices
-      JOIN customers ON invoices.customer_id = customers.id
-      WHERE
-        customers.name ILIKE ${`%${query}%`} OR
-        customers.email ILIKE ${`%${query}%`} OR
-        invoices.amount::text ILIKE ${`%${query}%`} OR
-        invoices.date::text ILIKE ${`%${query}%`} OR
-        invoices.status ILIKE ${`%${query}%`}
-      ORDER BY invoices.date DESC
-      LIMIT ${ITEMS_PER_PAGE} OFFSET ${offset}
-    ` as postgres.RowList<InvoicesTable[]>;
-
-    return invoices;
-  } catch (error) {
-    console.error('Database Error:', error);
-    throw new Error('Failed to fetch invoices.');
-  }
-}
-
-export async function fetchInvoicesPages(query: string) {
-    // This is equivalent to in fetch(..., {cache: 'no-store'}).
-    connection()
-  try {
-    const count = await sql`SELECT COUNT(*)
-    FROM invoices
-    JOIN customers ON invoices.customer_id = customers.id
-    WHERE
-      customers.name ILIKE ${`%${query}%`} OR
-      customers.email ILIKE ${`%${query}%`} OR
-      invoices.amount::text ILIKE ${`%${query}%`} OR
-      invoices.date::text ILIKE ${`%${query}%`} OR
-      invoices.status ILIKE ${`%${query}%`}
-  `;
-
-    const totalPages = Math.ceil(Number(count.at(0)?.count) / ITEMS_PER_PAGE);
-    return totalPages;
-  } catch (error) {
-    console.error('Database Error:', error);
-    throw new Error('Failed to fetch total number of invoices.');
-  }
-}
-
-export async function fetchInvoiceById(id: string) {
-    // This is equivalent to in fetch(..., {cache: 'no-store'}).
-    connection()
-  try {
-    const data = await sql`
-      SELECT
-        invoices.id,
-        invoices.customer_id,
-        invoices.amount,
-        invoices.status
-      FROM invoices
-      WHERE invoices.id = ${id};
-    ` as postgres.RowList<InvoiceForm[]>;
-
-    const invoice = data.map((invoice) => ({
-      ...invoice,
-      // Convert amount from cents to dollars
-      amount: invoice.amount / 100,
-    }));
-
-    return invoice[0];
-  } catch (error) {
-    console.error('Database Error:', error);
-    throw new Error('Failed to fetch invoice.');
-  }
-}
-
-export async function fetchFilteredCustomers(query: string) {
-    // This is equivalent to in fetch(..., {cache: 'no-store'}).
-    connection()
-  try {
-    const data = await sql`
-		SELECT
-		  customers.id,
-		  customers.name,
-		  customers.email,
-		  customers.image_url,
-		  COUNT(invoices.id) AS total_invoices,
-		  SUM(CASE WHEN invoices.status = 'pending' THEN invoices.amount ELSE 0 END) AS total_pending,
-		  SUM(CASE WHEN invoices.status = 'paid' THEN invoices.amount ELSE 0 END) AS total_paid
-		FROM customers
-		LEFT JOIN invoices ON customers.id = invoices.customer_id
-		WHERE
-		  customers.name ILIKE ${`%${query}%`} OR
-        customers.email ILIKE ${`%${query}%`}
-		GROUP BY customers.id, customers.name, customers.email, customers.image_url
-		ORDER BY customers.name ASC
-	  ` as postgres.RowList<CustomersTableType[]>;
-
-    const customers = data.map((customer) => ({
-      ...customer,
-      total_pending: formatCurrency(customer.total_pending),
-      total_paid: formatCurrency(customer.total_paid),
-    }));
-
-    return customers;
-  } catch (err) {
-    console.error('Database Error:', err);
-    throw new Error('Failed to fetch customer table.');
-  }
-}
diff --git a/src/app/lib/definitions.ts b/src/app/lib/definitions.ts
index 0860139..e4cf179 100644
--- a/src/app/lib/definitions.ts
+++ b/src/app/lib/definitions.ts
@@ -26,42 +26,6 @@ export type LatestInvoice = {
   amount: string;
 };
 
-// The database returns a number for amount, but we later format it to a string with the formatCurrency function
-export type LatestInvoiceRaw = Omit<LatestInvoice, 'amount'> & {
-  amount: number;
-};
-
-export type InvoicesTable = {
-  id: string;
-  customer_id: string;
-  name: string;
-  email: string;
-  image_url: string;
-  date: string;
-  amount: number;
-  status: 'pending' | 'paid';
-};
-
-export type CustomersTableType = {
-  id: string;
-  name: string;
-  email: string;
-  image_url: string;
-  total_invoices: number;
-  total_pending: number;
-  total_paid: number;
-};
-
-export type FormattedCustomersTable = {
-  id: string;
-  name: string;
-  email: string;
-  image_url: string;
-  total_invoices: number;
-  total_pending: string;
-  total_paid: string;
-};
-
 export type InvoiceForm = {
   id: string;
   customer_id: string;
diff --git a/src/feature/customer-invoice/data/module/customer-di.ts b/src/feature/customer-invoice/data/module/customer-di.ts
new file mode 100644
index 0000000..4d153a8
--- /dev/null
+++ b/src/feature/customer-invoice/data/module/customer-di.ts
@@ -0,0 +1,16 @@
+import di from "@/bootstrap/di/init-di";
+import CustomerDbRepo from "@/feature/customer/data/repo/customer-db-repo";
+import { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
+import fetchCustomersUsecase from "@/feature/customer/domain/usecase/fetch-customers-usecase";
+import { DependencyContainer } from "tsyringe";
+
+export default function getCustomerDi(): DependencyContainer {
+    const customerDi = di.createChildContainer()
+
+    customerDi.register(fetchCustomersUsecase.name, {
+        useValue: fetchCustomersUsecase
+    })
+
+    customerDi.register(customerRepoKey, CustomerDbRepo)
+    return customerDi
+}
\ No newline at end of file
diff --git a/src/feature/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/customer-invoice/data/repo/customer-invoice-db-repo.ts
new file mode 100644
index 0000000..bd52acc
--- /dev/null
+++ b/src/feature/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -0,0 +1,51 @@
+import { formatCurrency } from "@/app/lib/utils";
+import { sql } from "@/bootstrap/db/db";
+import CustomerInvoice from "@/feature/customer-invoice/domain/entity/customer-invoice";
+import CustomerInvoiceRepo from "@/feature/customer-invoice/domain/i-repo/customer-invoice-repo";
+import { connection } from "next/server";
+import postgres from "postgres";
+
+type customerInvoiceDbResponse = {
+  id: string;
+  name: string;
+  image_url: string;
+  email: string;
+  amount: string;
+}
+
+export default class CustomerDbRepo implements CustomerInvoiceRepo {
+    async fetchList(): Promise<CustomerInvoice[]> {
+        // This is equivalent to in fetch(..., {cache: 'no-store'}).
+        connection()
+
+        try {
+            const data = await sql`
+            SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
+            FROM invoices
+            JOIN customers ON invoices.customer_id = customers.id
+            ORDER BY invoices.date DESC
+            LIMIT 5` as postgres.RowList<customerInvoiceDbResponse[]>;
+
+            return this.customerInvoicesDto(data)
+        } catch (error) {
+            console.error('Database Error:', error);
+            throw new Error('Failed to fetch the latest invoices.');
+        }
+    }
+
+
+    private customerInvoicesDto(dbCustomers: customerInvoiceDbResponse[]): CustomerInvoice[] {
+        return  dbCustomers.map((customer) => this.customerInvoiceDto(customer));
+    }
+
+    private customerInvoiceDto(dbCustomer: customerInvoiceDbResponse): CustomerInvoice {
+        return new CustomerInvoice({
+            id: dbCustomer.id,
+            customerName: dbCustomer.name,
+            customerEmail: dbCustomer.email,
+            customerImageUrl: dbCustomer.image_url,
+            invoicesAmount: formatCurrency(+dbCustomer.amount),
+        })
+    }
+
+}
\ No newline at end of file
diff --git a/src/feature/customer-invoice/domain/entity/customer-invoice.ts b/src/feature/customer-invoice/domain/entity/customer-invoice.ts
new file mode 100644
index 0000000..783654c
--- /dev/null
+++ b/src/feature/customer-invoice/domain/entity/customer-invoice.ts
@@ -0,0 +1,21 @@
+export default class CustomerInvoice {
+    id: string;
+    customerName: string;
+    customerImageUrl: string;
+    customerEmail: string;
+    invoicesAmount: string;
+
+    constructor({
+        id,
+        customerEmail,
+        customerImageUrl,
+        customerName,
+        invoicesAmount
+    }: CustomerInvoice) {
+        this.id = id;
+        this.customerEmail = customerEmail
+        this.customerImageUrl = customerImageUrl
+        this.customerName = customerName
+        this.invoicesAmount = invoicesAmount
+    }
+}
\ No newline at end of file
diff --git a/src/feature/customer-invoice/domain/i-repo/customer-invoice-repo.ts b/src/feature/customer-invoice/domain/i-repo/customer-invoice-repo.ts
new file mode 100644
index 0000000..f3f5eba
--- /dev/null
+++ b/src/feature/customer-invoice/domain/i-repo/customer-invoice-repo.ts
@@ -0,0 +1,7 @@
+import CustomerInvoice from "@/feature/customer-invoice/domain/entity/customer-invoice"
+
+export default interface CustomerInvoiceRepo {
+    fetchList(): Promise<CustomerInvoice[]>
+}
+
+export const customerInvoiceRepoKey = "customerInvoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts b/src/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
new file mode 100644
index 0000000..1f16825
--- /dev/null
+++ b/src/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
@@ -0,0 +1,11 @@
+"use server"
+import serverDi from "@/feature/common/server-di";
+import CustomerInvoice from "@/feature/customer-invoice/domain/entity/customer-invoice";
+import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/customer-invoice/domain/i-repo/customer-invoice-repo";
+import { customerInvoiceModuleKey } from "@/feature/customer-invoice/invoice-module-key";
+
+export default function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> {
+    const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey)
+
+    return repo.fetchList()
+}
\ No newline at end of file
diff --git a/src/feature/customer-invoice/invoice-module-key.ts b/src/feature/customer-invoice/invoice-module-key.ts
new file mode 100644
index 0000000..6ab1512
--- /dev/null
+++ b/src/feature/customer-invoice/invoice-module-key.ts
@@ -0,0 +1 @@
+export const customerInvoiceModuleKey = "customerInvoiceModuleKey"
\ No newline at end of file
diff --git a/src/feature/customer/data/module/customer-di.ts b/src/feature/customer/data/module/customer-di.ts
index 05df3ae..4d153a8 100644
--- a/src/feature/customer/data/module/customer-di.ts
+++ b/src/feature/customer/data/module/customer-di.ts
@@ -1,4 +1,6 @@
 import di from "@/bootstrap/di/init-di";
+import CustomerDbRepo from "@/feature/customer/data/repo/customer-db-repo";
+import { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
 import fetchCustomersUsecase from "@/feature/customer/domain/usecase/fetch-customers-usecase";
 import { DependencyContainer } from "tsyringe";
 
@@ -9,5 +11,6 @@ export default function getCustomerDi(): DependencyContainer {
         useValue: fetchCustomersUsecase
     })
 
+    customerDi.register(customerRepoKey, CustomerDbRepo)
     return customerDi
 }
\ No newline at end of file
diff --git a/src/feature/customer/data/repo/customer-db-repo.ts b/src/feature/customer/data/repo/customer-db-repo.ts
new file mode 100644
index 0000000..fec27e1
--- /dev/null
+++ b/src/feature/customer/data/repo/customer-db-repo.ts
@@ -0,0 +1,66 @@
+import { formatCurrency } from "@/app/lib/utils";
+import { sql } from "@/bootstrap/db/db";
+import Customer from "@/feature/customer/domain/entity/customer";
+import CustomerRepo from "@/feature/customer/domain/i-repo/customer-repo";
+import { connection } from "next/server";
+import postgres from "postgres";
+
+type customerDbResponse = {
+  id: string;
+  name: string;
+  email: string;
+  image_url: string;
+  total_invoices: string;
+  total_pending: string;
+  total_paid: string;
+}
+
+export default class CustomerDbRepo implements CustomerRepo {
+    async fetchList(query: string): Promise<Customer[]> {
+        // This is equivalent to in fetch(..., {cache: 'no-store'}).
+        connection()
+        try {
+            const data = await sql`
+                SELECT
+                customers.id,
+                customers.name,
+                customers.email,
+                customers.image_url,
+                COUNT(invoices.id) AS total_invoices,
+                SUM(CASE WHEN invoices.status = 'pending' THEN invoices.amount ELSE 0 END) AS total_pending,
+                SUM(CASE WHEN invoices.status = 'paid' THEN invoices.amount ELSE 0 END) AS total_paid
+                FROM customers
+                LEFT JOIN invoices ON customers.id = invoices.customer_id
+                WHERE
+                customers.name ILIKE ${`%${query}%`} OR
+                customers.email ILIKE ${`%${query}%`}
+                GROUP BY customers.id, customers.name, customers.email, customers.image_url
+                ORDER BY customers.name ASC
+            ` as postgres.RowList<customerDbResponse[]>;
+
+
+            return this.customersDto(data);
+        } catch (err) {
+            console.error('Database Error:', err);
+            throw new Error('Failed to fetch customer table.');
+        }
+    }
+
+
+    private customersDto(dbCustomers: customerDbResponse[]): Customer[] {
+        return  dbCustomers.map((customer) => this.customerDto(customer));
+    }
+
+    private customerDto(dbCustomer: customerDbResponse): Customer {
+        return new Customer({
+            id: dbCustomer.id,
+            name: dbCustomer.name,
+            email: dbCustomer.email,
+            imageUrl: dbCustomer.image_url,
+            totalInvoices: dbCustomer.total_invoices,
+            totalPending: formatCurrency(Number(dbCustomer.total_pending)),
+            totalPaid: formatCurrency(Number(dbCustomer.total_paid)),
+        })
+    }
+
+}
\ No newline at end of file
diff --git a/src/feature/customer/domain/entity/customer.ts b/src/feature/customer/domain/entity/customer.ts
index 3b13a69..6f32ec3 100644
--- a/src/feature/customer/domain/entity/customer.ts
+++ b/src/feature/customer/domain/entity/customer.ts
@@ -1,21 +1,11 @@
-export type CustomersTableType = {
-  id: string;
-  name: string;
-  email: string;
-  image_url: string;
-  total_invoices: number;
-  total_pending: number;
-  total_paid: number;
-};
-
 export default class Customer {
     id: string;
     name: string;
     email: string;
     imageUrl: string;
-    totalInvoices: number;
-    totalPending: number;
-    totalPaid: number;
+    totalInvoices: string;
+    totalPending: string;
+    totalPaid: string;
 
     constructor({
         id,
diff --git a/src/test/common/fake-factory/customer/customer-fake-factory.ts b/src/test/common/fake-factory/customer/customer-fake-factory.ts
index be69bec..cf7a53c 100644
--- a/src/test/common/fake-factory/customer/customer-fake-factory.ts
+++ b/src/test/common/fake-factory/customer/customer-fake-factory.ts
@@ -8,9 +8,9 @@ export default class CustomerFakeFactory {
             name: faker.person.fullName(),
             email: faker.internet.email(),
             imageUrl: faker.image.url(),
-            totalInvoices: faker.number.int(),
-            totalPaid: faker.number.int(),
-            totalPending: faker.number.int(),
+            totalInvoices: faker.number.int().toLocaleString(),
+            totalPaid: faker.finance.amount(),
+            totalPending: faker.number.int().toLocaleString(),
         })
     }
 
-- 
2.39.5


From 67be77ab9c8e1e470e5b3897e0aeb0a3b23cb2c5 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 15:45:53 +0300
Subject: [PATCH 09/37] Add fetch amount of invoice module

---
 src/app/lib/data.ts                           | 24 -------------------
 src/app/lib/definitions.ts                    |  8 -------
 src/feature/common/server-di.ts               |  8 ++++++-
 .../data/module/customer-di.ts                | 16 -------------
 .../data/module/customer-invoice-di.ts        | 16 +++++++++++++
 src/feature/invoice/data/module/invoice-di.ts | 16 +++++++++++++
 .../invoice/data/repo/invoice-db-repo.ts      | 11 +++++++++
 .../invoice/domain/i-repo/invoice-repo.ts     |  6 +++++
 .../fetch-all-invoices-amount-usecase.ts      | 10 ++++++++
 src/feature/invoice/invoice-module-key.ts     |  1 +
 10 files changed, 67 insertions(+), 49 deletions(-)
 delete mode 100644 src/feature/customer-invoice/data/module/customer-di.ts
 create mode 100644 src/feature/customer-invoice/data/module/customer-invoice-di.ts
 create mode 100644 src/feature/invoice/data/module/invoice-di.ts
 create mode 100644 src/feature/invoice/data/repo/invoice-db-repo.ts
 create mode 100644 src/feature/invoice/domain/i-repo/invoice-repo.ts
 create mode 100644 src/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
 create mode 100644 src/feature/invoice/invoice-module-key.ts

diff --git a/src/app/lib/data.ts b/src/app/lib/data.ts
index d76cf24..b28fcb0 100644
--- a/src/app/lib/data.ts
+++ b/src/app/lib/data.ts
@@ -3,7 +3,6 @@ import {
   Revenue,
   Invoice,
   Customer,
-  LatestInvoice,
 } from './definitions';
 import { formatCurrency } from './utils';
 import postgres from 'postgres';
@@ -31,29 +30,6 @@ export async function fetchRevenue() {
   }
 }
 
-export async function fetchLatestInvoices() {
-  // This is equivalent to in fetch(..., {cache: 'no-store'}).
-  connection()
-
-  try {
-    const data = await sql`
-      SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
-      FROM invoices
-      JOIN customers ON invoices.customer_id = customers.id
-      ORDER BY invoices.date DESC
-      LIMIT 5` as postgres.RowList<LatestInvoice[]>;
-
-    const latestInvoices = data.map((invoice) => ({
-      ...invoice,
-      amount: formatCurrency(+invoice.amount),
-    }));
-    return latestInvoices;
-  } catch (error) {
-    console.error('Database Error:', error);
-    throw new Error('Failed to fetch the latest invoices.');
-  }
-}
-
 export async function fetchCardData() {
     // This is equivalent to in fetch(..., {cache: 'no-store'}).
   connection()
diff --git a/src/app/lib/definitions.ts b/src/app/lib/definitions.ts
index e4cf179..b26dd32 100644
--- a/src/app/lib/definitions.ts
+++ b/src/app/lib/definitions.ts
@@ -18,14 +18,6 @@ export type Revenue = {
   revenue: number;
 };
 
-export type LatestInvoice = {
-  id: string;
-  name: string;
-  image_url: string;
-  email: string;
-  amount: string;
-};
-
 export type InvoiceForm = {
   id: string;
   customer_id: string;
diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
index 78095a9..bd28b80 100644
--- a/src/feature/common/server-di.ts
+++ b/src/feature/common/server-di.ts
@@ -1,13 +1,19 @@
+import getCustomerInvoiceDi from "@/feature/customer-invoice/data/module/customer-invoice-di";
+import { customerInvoiceModuleKey } from "@/feature/customer-invoice/invoice-module-key";
 import { customerKey } from "@/feature/customer/customer-key";
 import getCustomerDi from "@/feature/customer/data/module/customer-di";
 import { testModuleKey } from "@/feature/domain/test/test-module-key";
 import getTestModule from "@/feature/infra/test/module/test-module";
+import getInvoiceDi from "@/feature/invoice/data/module/invoice-di";
+import { invoiceModuleKey } from "@/feature/invoice/invoice-module-key";
 import { DependencyContainer } from "tsyringe";
 
 export default function serverDi(module: string): DependencyContainer {
     const getDi = {
         [testModuleKey]: getTestModule,
-        [customerKey]: getCustomerDi
+        [customerKey]: getCustomerDi,
+        [customerInvoiceModuleKey]: getCustomerInvoiceDi,
+        [invoiceModuleKey]: getInvoiceDi,
     }[module]
 
     if (!getDi) throw new Error("Server Di didn't found for module: " + module)
diff --git a/src/feature/customer-invoice/data/module/customer-di.ts b/src/feature/customer-invoice/data/module/customer-di.ts
deleted file mode 100644
index 4d153a8..0000000
--- a/src/feature/customer-invoice/data/module/customer-di.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import di from "@/bootstrap/di/init-di";
-import CustomerDbRepo from "@/feature/customer/data/repo/customer-db-repo";
-import { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
-import fetchCustomersUsecase from "@/feature/customer/domain/usecase/fetch-customers-usecase";
-import { DependencyContainer } from "tsyringe";
-
-export default function getCustomerDi(): DependencyContainer {
-    const customerDi = di.createChildContainer()
-
-    customerDi.register(fetchCustomersUsecase.name, {
-        useValue: fetchCustomersUsecase
-    })
-
-    customerDi.register(customerRepoKey, CustomerDbRepo)
-    return customerDi
-}
\ No newline at end of file
diff --git a/src/feature/customer-invoice/data/module/customer-invoice-di.ts b/src/feature/customer-invoice/data/module/customer-invoice-di.ts
new file mode 100644
index 0000000..fa6712e
--- /dev/null
+++ b/src/feature/customer-invoice/data/module/customer-invoice-di.ts
@@ -0,0 +1,16 @@
+import di from "@/bootstrap/di/init-di";
+import { customerInvoiceRepoKey } from "@/feature/customer-invoice/domain/i-repo/customer-invoice-repo";
+import fetchCustomerInvoicesUsecase from "@/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
+import CustomerDbRepo from "@/feature/customer/data/repo/customer-db-repo";
+import { DependencyContainer } from "tsyringe";
+
+export default function getCustomerInvoiceInvoiceDi(): DependencyContainer {
+    const customerInvoiceDi = di.createChildContainer()
+
+    customerInvoiceDi.register(fetchCustomerInvoicesUsecase.name, {
+        useValue: fetchCustomerInvoicesUsecase
+    })
+
+    customerInvoiceDi.register(customerInvoiceRepoKey, CustomerDbRepo)
+    return customerInvoiceDi
+}
\ No newline at end of file
diff --git a/src/feature/invoice/data/module/invoice-di.ts b/src/feature/invoice/data/module/invoice-di.ts
new file mode 100644
index 0000000..28ce2e0
--- /dev/null
+++ b/src/feature/invoice/data/module/invoice-di.ts
@@ -0,0 +1,16 @@
+import di from "@/bootstrap/di/init-di";
+import invoiceDbRepo from "@/feature/invoice/data/repo/invoice-db-repo";
+import { invoiceRepoKey } from "@/feature/invoice/domain/i-repo/invoice-repo";
+import fetchAllInvoicesAmountUsecase from "@/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase";
+import { DependencyContainer } from "tsyringe";
+
+export default function getInvoiceDi(): DependencyContainer {
+    const invoiceDi = di.createChildContainer()
+
+    invoiceDi.register(fetchAllInvoicesAmountUsecase.name, {
+        useValue: fetchAllInvoicesAmountUsecase 
+    })
+
+    invoiceDi.register(invoiceRepoKey, invoiceDbRepo)
+    return invoiceDi
+}
\ No newline at end of file
diff --git a/src/feature/invoice/data/repo/invoice-db-repo.ts b/src/feature/invoice/data/repo/invoice-db-repo.ts
new file mode 100644
index 0000000..5191d67
--- /dev/null
+++ b/src/feature/invoice/data/repo/invoice-db-repo.ts
@@ -0,0 +1,11 @@
+import { sql } from "@/bootstrap/db/db";
+import InvoiceRepo from "@/feature/invoice/domain/i-repo/invoice-repo";
+import postgres from "postgres";
+
+export default class InvoiceDbRepo implements InvoiceRepo {
+    async fetchAllInvoicesAmount(): Promise<number> {
+        const data = await sql`SELECT COUNT(*) FROM invoices` as postgres.RowList<unknown[]>;
+
+        return data.count ?? 0
+    }
+}
\ No newline at end of file
diff --git a/src/feature/invoice/domain/i-repo/invoice-repo.ts b/src/feature/invoice/domain/i-repo/invoice-repo.ts
new file mode 100644
index 0000000..fb4c8a2
--- /dev/null
+++ b/src/feature/invoice/domain/i-repo/invoice-repo.ts
@@ -0,0 +1,6 @@
+
+export default interface InvoiceRepo {
+    fetchAllInvoicesAmount(): Promise<number>
+}
+
+export const invoiceRepoKey = "invoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts b/src/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
new file mode 100644
index 0000000..1922f81
--- /dev/null
+++ b/src/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
@@ -0,0 +1,10 @@
+"use server"
+import serverDi from "@/feature/common/server-di";
+import InvoiceRepo, { invoiceRepoKey } from "@/feature/invoice/domain/i-repo/invoice-repo";
+import { invoiceModuleKey } from "@/feature/invoice/invoice-module-key";
+
+export default function fetchAllInvoicesAmountUsecase(): Promise<number> {
+    const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
+
+    return repo.fetchAllInvoicesAmount()
+}
\ No newline at end of file
diff --git a/src/feature/invoice/invoice-module-key.ts b/src/feature/invoice/invoice-module-key.ts
new file mode 100644
index 0000000..c9cafd9
--- /dev/null
+++ b/src/feature/invoice/invoice-module-key.ts
@@ -0,0 +1 @@
+export const invoiceModuleKey = "invoiceModuleKey"
\ No newline at end of file
-- 
2.39.5


From 5704cf6ff95a50a8a1e36ac891b312c8a758f23b Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 16:02:29 +0300
Subject: [PATCH 10/37] Add fetch invoices status summary module

---
 src/app/lib/data.ts                           |  3 ++-
 .../data/module/customer-invoice-di.ts        |  2 +-
 src/feature/invoice/data/module/invoice-di.ts |  5 -----
 .../invoice/data/repo/invoice-db-repo.ts      | 22 ++++++++++++++++++-
 .../invoice/domain/i-repo/invoice-repo.ts     |  2 ++
 .../usecase/fetch-invoices-status-summary.ts  |  9 ++++++++
 .../domain/value-object/invoice-status.ts     | 12 ++++++++++
 7 files changed, 47 insertions(+), 8 deletions(-)
 create mode 100644 src/feature/invoice/domain/usecase/fetch-invoices-status-summary.ts
 create mode 100644 src/feature/invoice/domain/value-object/invoice-status.ts

diff --git a/src/app/lib/data.ts b/src/app/lib/data.ts
index b28fcb0..6326383 100644
--- a/src/app/lib/data.ts
+++ b/src/app/lib/data.ts
@@ -41,6 +41,7 @@ export async function fetchCardData() {
     const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
     const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
     const invoiceStatusPromise = sql`SELECT
+         id,
          SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
          SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
          FROM invoices`;
@@ -53,7 +54,7 @@ export async function fetchCardData() {
 
     const invoices = data[0] as postgres.RowList<Invoice[]>
     const customres = data[1] as postgres.RowList<Customer[]>
-    const invoiceStatus = data[2] as postgres.RowList<({paid: string, pending: string})[]>
+    const invoiceStatus = data[2] as postgres.RowList<({id: string; paid: string, pending: string})[]>
     const numberOfInvoices = Number(invoices.count ?? '0');
     const numberOfCustomers = Number(customres.count ?? '0');
     const totalPaidInvoices = formatCurrency(Number(invoiceStatus.at(0)?.paid ?? '0'));
diff --git a/src/feature/customer-invoice/data/module/customer-invoice-di.ts b/src/feature/customer-invoice/data/module/customer-invoice-di.ts
index fa6712e..d1a93f1 100644
--- a/src/feature/customer-invoice/data/module/customer-invoice-di.ts
+++ b/src/feature/customer-invoice/data/module/customer-invoice-di.ts
@@ -4,7 +4,7 @@ import fetchCustomerInvoicesUsecase from "@/feature/customer-invoice/domain/usec
 import CustomerDbRepo from "@/feature/customer/data/repo/customer-db-repo";
 import { DependencyContainer } from "tsyringe";
 
-export default function getCustomerInvoiceInvoiceDi(): DependencyContainer {
+export default function getCustomerInvoiceDi(): DependencyContainer {
     const customerInvoiceDi = di.createChildContainer()
 
     customerInvoiceDi.register(fetchCustomerInvoicesUsecase.name, {
diff --git a/src/feature/invoice/data/module/invoice-di.ts b/src/feature/invoice/data/module/invoice-di.ts
index 28ce2e0..c7c133a 100644
--- a/src/feature/invoice/data/module/invoice-di.ts
+++ b/src/feature/invoice/data/module/invoice-di.ts
@@ -1,16 +1,11 @@
 import di from "@/bootstrap/di/init-di";
 import invoiceDbRepo from "@/feature/invoice/data/repo/invoice-db-repo";
 import { invoiceRepoKey } from "@/feature/invoice/domain/i-repo/invoice-repo";
-import fetchAllInvoicesAmountUsecase from "@/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase";
 import { DependencyContainer } from "tsyringe";
 
 export default function getInvoiceDi(): DependencyContainer {
     const invoiceDi = di.createChildContainer()
 
-    invoiceDi.register(fetchAllInvoicesAmountUsecase.name, {
-        useValue: fetchAllInvoicesAmountUsecase 
-    })
-
     invoiceDi.register(invoiceRepoKey, invoiceDbRepo)
     return invoiceDi
 }
\ No newline at end of file
diff --git a/src/feature/invoice/data/repo/invoice-db-repo.ts b/src/feature/invoice/data/repo/invoice-db-repo.ts
index 5191d67..bc7c2a8 100644
--- a/src/feature/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/invoice/data/repo/invoice-db-repo.ts
@@ -1,11 +1,31 @@
+import { formatCurrency } from "@/app/lib/utils";
 import { sql } from "@/bootstrap/db/db";
 import InvoiceRepo from "@/feature/invoice/domain/i-repo/invoice-repo";
+import InvoiceStatusSummary from "@/feature/invoice/domain/value-object/invoice-status";
 import postgres from "postgres";
 
+type InvoiceSummaryDbResponse = {paid: string, pending: string}
 export default class InvoiceDbRepo implements InvoiceRepo {
     async fetchAllInvoicesAmount(): Promise<number> {
         const data = await sql`SELECT COUNT(*) FROM invoices` as postgres.RowList<unknown[]>;
-
+        
         return data.count ?? 0
     }
+
+    async fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
+        const invoiceStatusPromise = await sql`SELECT
+            SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
+            SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
+            FROM invoices` as postgres.RowList<InvoiceSummaryDbResponse[]>;
+        
+        return this.invoiceSummaryDto(invoiceStatusPromise.at(0))
+        
+    }
+
+    private invoiceSummaryDto(dbResponse?: InvoiceSummaryDbResponse): InvoiceStatusSummary {
+        return new InvoiceStatusSummary({
+            paid: formatCurrency(Number(dbResponse?.paid ?? '0')),
+            pending: formatCurrency(Number(dbResponse?.pending ?? '0'))
+        })
+    }
 }
\ No newline at end of file
diff --git a/src/feature/invoice/domain/i-repo/invoice-repo.ts b/src/feature/invoice/domain/i-repo/invoice-repo.ts
index fb4c8a2..085421c 100644
--- a/src/feature/invoice/domain/i-repo/invoice-repo.ts
+++ b/src/feature/invoice/domain/i-repo/invoice-repo.ts
@@ -1,6 +1,8 @@
+import InvoiceStatusSummary from "@/feature/invoice/domain/value-object/invoice-status"
 
 export default interface InvoiceRepo {
     fetchAllInvoicesAmount(): Promise<number>
+    fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary>
 }
 
 export const invoiceRepoKey = "invoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/invoice/domain/usecase/fetch-invoices-status-summary.ts b/src/feature/invoice/domain/usecase/fetch-invoices-status-summary.ts
new file mode 100644
index 0000000..f35a4dc
--- /dev/null
+++ b/src/feature/invoice/domain/usecase/fetch-invoices-status-summary.ts
@@ -0,0 +1,9 @@
+import serverDi from "@/feature/common/server-di";
+import InvoiceRepo, { invoiceRepoKey } from "@/feature/invoice/domain/i-repo/invoice-repo";
+import InvoiceStatusSummary from "@/feature/invoice/domain/value-object/invoice-status";
+import { invoiceModuleKey } from "@/feature/invoice/invoice-module-key";
+
+export default function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
+    const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
+    return repo.fetchInvoicesStatusSummary()
+}
\ No newline at end of file
diff --git a/src/feature/invoice/domain/value-object/invoice-status.ts b/src/feature/invoice/domain/value-object/invoice-status.ts
new file mode 100644
index 0000000..b2c393c
--- /dev/null
+++ b/src/feature/invoice/domain/value-object/invoice-status.ts
@@ -0,0 +1,12 @@
+export default class InvoiceStatusSummary {
+    paid: string;
+    pending: string;
+
+    constructor({
+        paid,
+        pending
+    }: InvoiceStatusSummary) {
+        this.paid = paid;
+        this.pending = pending;
+    }
+}
\ No newline at end of file
-- 
2.39.5


From b6a15e0579a9c834a7c46925869ae1d41d4b7e11 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 16:06:43 +0300
Subject: [PATCH 11/37] Add fetch all customers module

---
 src/feature/customer/data/repo/customer-db-repo.ts        | 5 +++++
 src/feature/customer/domain/i-repo/customer-repo.ts       | 1 +
 .../domain/usecase/fetch-customers-amount-usecase.ts      | 8 ++++++++
 3 files changed, 14 insertions(+)
 create mode 100644 src/feature/customer/domain/usecase/fetch-customers-amount-usecase.ts

diff --git a/src/feature/customer/data/repo/customer-db-repo.ts b/src/feature/customer/data/repo/customer-db-repo.ts
index fec27e1..f69239a 100644
--- a/src/feature/customer/data/repo/customer-db-repo.ts
+++ b/src/feature/customer/data/repo/customer-db-repo.ts
@@ -46,6 +46,11 @@ export default class CustomerDbRepo implements CustomerRepo {
         }
     }
 
+    async fetchCustomersAmount(): Promise<number> {
+        const data = await sql`SELECT COUNT(*) FROM customers`as postgres.RowList<unknown[]>;
+        return Number(data.count ?? '0');
+    }
+
 
     private customersDto(dbCustomers: customerDbResponse[]): Customer[] {
         return  dbCustomers.map((customer) => this.customerDto(customer));
diff --git a/src/feature/customer/domain/i-repo/customer-repo.ts b/src/feature/customer/domain/i-repo/customer-repo.ts
index 5c9cbf2..08e8226 100644
--- a/src/feature/customer/domain/i-repo/customer-repo.ts
+++ b/src/feature/customer/domain/i-repo/customer-repo.ts
@@ -2,6 +2,7 @@ import Customer from "@/feature/customer/domain/entity/customer"
 
 export default interface CustomerRepo {
     fetchList(query: string): Promise<Customer[]>
+    fetchCustomersAmount(): Promise<number>
 }
 
 export const customerRepoKey = "customerRepoKey"
\ No newline at end of file
diff --git a/src/feature/customer/domain/usecase/fetch-customers-amount-usecase.ts b/src/feature/customer/domain/usecase/fetch-customers-amount-usecase.ts
new file mode 100644
index 0000000..608ae37
--- /dev/null
+++ b/src/feature/customer/domain/usecase/fetch-customers-amount-usecase.ts
@@ -0,0 +1,8 @@
+import serverDi from "@/feature/common/server-di";
+import { customerKey } from "@/feature/customer/customer-key";
+import CustomerRepo, { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
+
+export default function fetchCustomersAmountUsecase(): Promise<number> {
+    const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
+    return repo.fetchCustomersAmount()
+}
\ No newline at end of file
-- 
2.39.5


From 2d8774ae3634570721e9440e50f0cb798b61d209 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 16:08:19 +0300
Subject: [PATCH 12/37] Add core subdomain and fix location of domains in
 feature layer

---
 src/feature/common/server-di.ts                      | 12 ++++++------
 .../data/module/customer-invoice-di.ts               |  6 +++---
 .../data/repo/customer-invoice-db-repo.ts            |  4 ++--
 .../domain/entity/customer-invoice.ts                |  0
 .../domain/i-repo/customer-invoice-repo.ts           |  7 +++++++
 .../usecase/fetch-customer-invoices-usecase.ts       |  6 +++---
 .../customer-invoice/invoice-module-key.ts           |  0
 src/feature/{ => core}/customer/customer-key.ts      |  0
 .../{ => core}/customer/data/module/customer-di.ts   |  6 +++---
 .../customer/data/repo/customer-db-repo.ts           |  4 ++--
 .../{ => core}/customer/domain/entity/customer.ts    |  0
 .../customer/domain/i-repo/customer-repo.ts          |  2 +-
 .../domain/usecase/fetch-customers-amount-usecase.ts |  4 ++--
 .../domain/usecase/fetch-customers-usecase.ts        |  6 +++---
 .../{ => core}/invoice/data/module/invoice-di.ts     |  4 ++--
 .../{ => core}/invoice/data/repo/invoice-db-repo.ts  |  4 ++--
 .../{ => core}/invoice/domain/i-repo/invoice-repo.ts |  2 +-
 .../usecase/fetch-all-invoices-amount-usecase.ts     |  4 ++--
 .../domain/usecase/fetch-invoices-status-summary.ts  |  9 +++++++++
 .../invoice/domain/value-object/invoice-status.ts    |  0
 src/feature/{ => core}/invoice/invoice-module-key.ts |  0
 .../domain/i-repo/customer-invoice-repo.ts           |  7 -------
 .../domain/usecase/fetch-invoices-status-summary.ts  |  9 ---------
 .../fake-factory/customer/customer-fake-factory.ts   |  2 +-
 .../domain/usecase/fetch-customers-usecase.test.ts   |  4 ++--
 25 files changed, 51 insertions(+), 51 deletions(-)
 rename src/feature/{ => core}/customer-invoice/data/module/customer-invoice-di.ts (58%)
 rename src/feature/{ => core}/customer-invoice/data/repo/customer-invoice-db-repo.ts (89%)
 rename src/feature/{ => core}/customer-invoice/domain/entity/customer-invoice.ts (100%)
 create mode 100644 src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
 rename src/feature/{ => core}/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts (57%)
 rename src/feature/{ => core}/customer-invoice/invoice-module-key.ts (100%)
 rename src/feature/{ => core}/customer/customer-key.ts (100%)
 rename src/feature/{ => core}/customer/data/module/customer-di.ts (58%)
 rename src/feature/{ => core}/customer/data/repo/customer-db-repo.ts (94%)
 rename src/feature/{ => core}/customer/domain/entity/customer.ts (100%)
 rename src/feature/{ => core}/customer/domain/i-repo/customer-repo.ts (53%)
 rename src/feature/{ => core}/customer/domain/usecase/fetch-customers-amount-usecase.ts (58%)
 rename src/feature/{ => core}/customer/domain/usecase/fetch-customers-usecase.ts (52%)
 rename src/feature/{ => core}/invoice/data/module/invoice-di.ts (62%)
 rename src/feature/{ => core}/invoice/data/repo/invoice-db-repo.ts (87%)
 rename src/feature/{ => core}/invoice/domain/i-repo/invoice-repo.ts (52%)
 rename src/feature/{ => core}/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts (59%)
 create mode 100644 src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
 rename src/feature/{ => core}/invoice/domain/value-object/invoice-status.ts (100%)
 rename src/feature/{ => core}/invoice/invoice-module-key.ts (100%)
 delete mode 100644 src/feature/customer-invoice/domain/i-repo/customer-invoice-repo.ts
 delete mode 100644 src/feature/invoice/domain/usecase/fetch-invoices-status-summary.ts

diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
index bd28b80..af68905 100644
--- a/src/feature/common/server-di.ts
+++ b/src/feature/common/server-di.ts
@@ -1,11 +1,11 @@
-import getCustomerInvoiceDi from "@/feature/customer-invoice/data/module/customer-invoice-di";
-import { customerInvoiceModuleKey } from "@/feature/customer-invoice/invoice-module-key";
-import { customerKey } from "@/feature/customer/customer-key";
-import getCustomerDi from "@/feature/customer/data/module/customer-di";
+import getCustomerInvoiceDi from "@/feature/core/customer-invoice/data/module/customer-invoice-di";
+import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
+import { customerKey } from "@/feature/core/customer/customer-key";
+import getCustomerDi from "@/feature/core/customer/data/module/customer-di";
 import { testModuleKey } from "@/feature/domain/test/test-module-key";
 import getTestModule from "@/feature/infra/test/module/test-module";
-import getInvoiceDi from "@/feature/invoice/data/module/invoice-di";
-import { invoiceModuleKey } from "@/feature/invoice/invoice-module-key";
+import getInvoiceDi from "@/feature/core/invoice/data/module/invoice-di";
+import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 import { DependencyContainer } from "tsyringe";
 
 export default function serverDi(module: string): DependencyContainer {
diff --git a/src/feature/customer-invoice/data/module/customer-invoice-di.ts b/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
similarity index 58%
rename from src/feature/customer-invoice/data/module/customer-invoice-di.ts
rename to src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
index d1a93f1..76a7453 100644
--- a/src/feature/customer-invoice/data/module/customer-invoice-di.ts
+++ b/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
@@ -1,7 +1,7 @@
 import di from "@/bootstrap/di/init-di";
-import { customerInvoiceRepoKey } from "@/feature/customer-invoice/domain/i-repo/customer-invoice-repo";
-import fetchCustomerInvoicesUsecase from "@/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
-import CustomerDbRepo from "@/feature/customer/data/repo/customer-db-repo";
+import { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
+import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
+import CustomerDbRepo from "@/feature/core/customer/data/repo/customer-db-repo";
 import { DependencyContainer } from "tsyringe";
 
 export default function getCustomerInvoiceDi(): DependencyContainer {
diff --git a/src/feature/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
similarity index 89%
rename from src/feature/customer-invoice/data/repo/customer-invoice-db-repo.ts
rename to src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
index bd52acc..45ec6ab 100644
--- a/src/feature/customer-invoice/data/repo/customer-invoice-db-repo.ts
+++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -1,7 +1,7 @@
 import { formatCurrency } from "@/app/lib/utils";
 import { sql } from "@/bootstrap/db/db";
-import CustomerInvoice from "@/feature/customer-invoice/domain/entity/customer-invoice";
-import CustomerInvoiceRepo from "@/feature/customer-invoice/domain/i-repo/customer-invoice-repo";
+import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
+import CustomerInvoiceRepo from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
 import { connection } from "next/server";
 import postgres from "postgres";
 
diff --git a/src/feature/customer-invoice/domain/entity/customer-invoice.ts b/src/feature/core/customer-invoice/domain/entity/customer-invoice.ts
similarity index 100%
rename from src/feature/customer-invoice/domain/entity/customer-invoice.ts
rename to src/feature/core/customer-invoice/domain/entity/customer-invoice.ts
diff --git a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
new file mode 100644
index 0000000..0a8c993
--- /dev/null
+++ b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
@@ -0,0 +1,7 @@
+import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice"
+
+export default interface CustomerInvoiceRepo {
+    fetchList(): Promise<CustomerInvoice[]>
+}
+
+export const customerInvoiceRepoKey = "customerInvoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
similarity index 57%
rename from src/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
rename to src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
index 1f16825..fd97f46 100644
--- a/src/feature/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
+++ b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
@@ -1,8 +1,8 @@
 "use server"
 import serverDi from "@/feature/common/server-di";
-import CustomerInvoice from "@/feature/customer-invoice/domain/entity/customer-invoice";
-import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/customer-invoice/domain/i-repo/customer-invoice-repo";
-import { customerInvoiceModuleKey } from "@/feature/customer-invoice/invoice-module-key";
+import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
+import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
+import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
 
 export default function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> {
     const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey)
diff --git a/src/feature/customer-invoice/invoice-module-key.ts b/src/feature/core/customer-invoice/invoice-module-key.ts
similarity index 100%
rename from src/feature/customer-invoice/invoice-module-key.ts
rename to src/feature/core/customer-invoice/invoice-module-key.ts
diff --git a/src/feature/customer/customer-key.ts b/src/feature/core/customer/customer-key.ts
similarity index 100%
rename from src/feature/customer/customer-key.ts
rename to src/feature/core/customer/customer-key.ts
diff --git a/src/feature/customer/data/module/customer-di.ts b/src/feature/core/customer/data/module/customer-di.ts
similarity index 58%
rename from src/feature/customer/data/module/customer-di.ts
rename to src/feature/core/customer/data/module/customer-di.ts
index 4d153a8..1fa4398 100644
--- a/src/feature/customer/data/module/customer-di.ts
+++ b/src/feature/core/customer/data/module/customer-di.ts
@@ -1,7 +1,7 @@
 import di from "@/bootstrap/di/init-di";
-import CustomerDbRepo from "@/feature/customer/data/repo/customer-db-repo";
-import { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
-import fetchCustomersUsecase from "@/feature/customer/domain/usecase/fetch-customers-usecase";
+import CustomerDbRepo from "@/feature/core/customer/data/repo/customer-db-repo";
+import { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
+import fetchCustomersUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-usecase";
 import { DependencyContainer } from "tsyringe";
 
 export default function getCustomerDi(): DependencyContainer {
diff --git a/src/feature/customer/data/repo/customer-db-repo.ts b/src/feature/core/customer/data/repo/customer-db-repo.ts
similarity index 94%
rename from src/feature/customer/data/repo/customer-db-repo.ts
rename to src/feature/core/customer/data/repo/customer-db-repo.ts
index f69239a..82c0494 100644
--- a/src/feature/customer/data/repo/customer-db-repo.ts
+++ b/src/feature/core/customer/data/repo/customer-db-repo.ts
@@ -1,7 +1,7 @@
 import { formatCurrency } from "@/app/lib/utils";
 import { sql } from "@/bootstrap/db/db";
-import Customer from "@/feature/customer/domain/entity/customer";
-import CustomerRepo from "@/feature/customer/domain/i-repo/customer-repo";
+import Customer from "@/feature/core/customer/domain/entity/customer";
+import CustomerRepo from "@/feature/core/customer/domain/i-repo/customer-repo";
 import { connection } from "next/server";
 import postgres from "postgres";
 
diff --git a/src/feature/customer/domain/entity/customer.ts b/src/feature/core/customer/domain/entity/customer.ts
similarity index 100%
rename from src/feature/customer/domain/entity/customer.ts
rename to src/feature/core/customer/domain/entity/customer.ts
diff --git a/src/feature/customer/domain/i-repo/customer-repo.ts b/src/feature/core/customer/domain/i-repo/customer-repo.ts
similarity index 53%
rename from src/feature/customer/domain/i-repo/customer-repo.ts
rename to src/feature/core/customer/domain/i-repo/customer-repo.ts
index 08e8226..d48fdde 100644
--- a/src/feature/customer/domain/i-repo/customer-repo.ts
+++ b/src/feature/core/customer/domain/i-repo/customer-repo.ts
@@ -1,4 +1,4 @@
-import Customer from "@/feature/customer/domain/entity/customer"
+import Customer from "@/feature/core/customer/domain/entity/customer"
 
 export default interface CustomerRepo {
     fetchList(query: string): Promise<Customer[]>
diff --git a/src/feature/customer/domain/usecase/fetch-customers-amount-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
similarity index 58%
rename from src/feature/customer/domain/usecase/fetch-customers-amount-usecase.ts
rename to src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
index 608ae37..f90d5c3 100644
--- a/src/feature/customer/domain/usecase/fetch-customers-amount-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
@@ -1,6 +1,6 @@
 import serverDi from "@/feature/common/server-di";
-import { customerKey } from "@/feature/customer/customer-key";
-import CustomerRepo, { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
+import { customerKey } from "@/feature/core/customer/customer-key";
+import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
 
 export default function fetchCustomersAmountUsecase(): Promise<number> {
     const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
diff --git a/src/feature/customer/domain/usecase/fetch-customers-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
similarity index 52%
rename from src/feature/customer/domain/usecase/fetch-customers-usecase.ts
rename to src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
index bf95239..60bd0c5 100644
--- a/src/feature/customer/domain/usecase/fetch-customers-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
@@ -1,9 +1,9 @@
 "use server"
 
 import serverDi from "@/feature/common/server-di";
-import { customerKey } from "@/feature/customer/customer-key";
-import Customer from "@/feature/customer/domain/entity/customer";
-import CustomerRepo, { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
+import { customerKey } from "@/feature/core/customer/customer-key";
+import Customer from "@/feature/core/customer/domain/entity/customer";
+import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
 
 export default function fetchCustomersUsecase(query: string): Promise<Customer[]> {
     const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
diff --git a/src/feature/invoice/data/module/invoice-di.ts b/src/feature/core/invoice/data/module/invoice-di.ts
similarity index 62%
rename from src/feature/invoice/data/module/invoice-di.ts
rename to src/feature/core/invoice/data/module/invoice-di.ts
index c7c133a..1434954 100644
--- a/src/feature/invoice/data/module/invoice-di.ts
+++ b/src/feature/core/invoice/data/module/invoice-di.ts
@@ -1,6 +1,6 @@
 import di from "@/bootstrap/di/init-di";
-import invoiceDbRepo from "@/feature/invoice/data/repo/invoice-db-repo";
-import { invoiceRepoKey } from "@/feature/invoice/domain/i-repo/invoice-repo";
+import invoiceDbRepo from "@/feature/core/invoice/data/repo/invoice-db-repo";
+import { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { DependencyContainer } from "tsyringe";
 
 export default function getInvoiceDi(): DependencyContainer {
diff --git a/src/feature/invoice/data/repo/invoice-db-repo.ts b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
similarity index 87%
rename from src/feature/invoice/data/repo/invoice-db-repo.ts
rename to src/feature/core/invoice/data/repo/invoice-db-repo.ts
index bc7c2a8..c9bf5e9 100644
--- a/src/feature/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
@@ -1,7 +1,7 @@
 import { formatCurrency } from "@/app/lib/utils";
 import { sql } from "@/bootstrap/db/db";
-import InvoiceRepo from "@/feature/invoice/domain/i-repo/invoice-repo";
-import InvoiceStatusSummary from "@/feature/invoice/domain/value-object/invoice-status";
+import InvoiceRepo from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
 import postgres from "postgres";
 
 type InvoiceSummaryDbResponse = {paid: string, pending: string}
diff --git a/src/feature/invoice/domain/i-repo/invoice-repo.ts b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
similarity index 52%
rename from src/feature/invoice/domain/i-repo/invoice-repo.ts
rename to src/feature/core/invoice/domain/i-repo/invoice-repo.ts
index 085421c..90bac8f 100644
--- a/src/feature/invoice/domain/i-repo/invoice-repo.ts
+++ b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
@@ -1,4 +1,4 @@
-import InvoiceStatusSummary from "@/feature/invoice/domain/value-object/invoice-status"
+import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status"
 
 export default interface InvoiceRepo {
     fetchAllInvoicesAmount(): Promise<number>
diff --git a/src/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts b/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
similarity index 59%
rename from src/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
rename to src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
index 1922f81..93e2deb 100644
--- a/src/feature/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
+++ b/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
@@ -1,7 +1,7 @@
 "use server"
 import serverDi from "@/feature/common/server-di";
-import InvoiceRepo, { invoiceRepoKey } from "@/feature/invoice/domain/i-repo/invoice-repo";
-import { invoiceModuleKey } from "@/feature/invoice/invoice-module-key";
+import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 
 export default function fetchAllInvoicesAmountUsecase(): Promise<number> {
     const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
diff --git a/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts b/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
new file mode 100644
index 0000000..07c76a8
--- /dev/null
+++ b/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
@@ -0,0 +1,9 @@
+import serverDi from "@/feature/common/server-di";
+import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
+import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
+
+export default function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
+    const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
+    return repo.fetchInvoicesStatusSummary()
+}
\ No newline at end of file
diff --git a/src/feature/invoice/domain/value-object/invoice-status.ts b/src/feature/core/invoice/domain/value-object/invoice-status.ts
similarity index 100%
rename from src/feature/invoice/domain/value-object/invoice-status.ts
rename to src/feature/core/invoice/domain/value-object/invoice-status.ts
diff --git a/src/feature/invoice/invoice-module-key.ts b/src/feature/core/invoice/invoice-module-key.ts
similarity index 100%
rename from src/feature/invoice/invoice-module-key.ts
rename to src/feature/core/invoice/invoice-module-key.ts
diff --git a/src/feature/customer-invoice/domain/i-repo/customer-invoice-repo.ts b/src/feature/customer-invoice/domain/i-repo/customer-invoice-repo.ts
deleted file mode 100644
index f3f5eba..0000000
--- a/src/feature/customer-invoice/domain/i-repo/customer-invoice-repo.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import CustomerInvoice from "@/feature/customer-invoice/domain/entity/customer-invoice"
-
-export default interface CustomerInvoiceRepo {
-    fetchList(): Promise<CustomerInvoice[]>
-}
-
-export const customerInvoiceRepoKey = "customerInvoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/invoice/domain/usecase/fetch-invoices-status-summary.ts b/src/feature/invoice/domain/usecase/fetch-invoices-status-summary.ts
deleted file mode 100644
index f35a4dc..0000000
--- a/src/feature/invoice/domain/usecase/fetch-invoices-status-summary.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import serverDi from "@/feature/common/server-di";
-import InvoiceRepo, { invoiceRepoKey } from "@/feature/invoice/domain/i-repo/invoice-repo";
-import InvoiceStatusSummary from "@/feature/invoice/domain/value-object/invoice-status";
-import { invoiceModuleKey } from "@/feature/invoice/invoice-module-key";
-
-export default function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
-    const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
-    return repo.fetchInvoicesStatusSummary()
-}
\ No newline at end of file
diff --git a/src/test/common/fake-factory/customer/customer-fake-factory.ts b/src/test/common/fake-factory/customer/customer-fake-factory.ts
index cf7a53c..319c223 100644
--- a/src/test/common/fake-factory/customer/customer-fake-factory.ts
+++ b/src/test/common/fake-factory/customer/customer-fake-factory.ts
@@ -1,4 +1,4 @@
-import Customer from "@/feature/customer/domain/entity/customer";
+import Customer from "@/feature/core/customer/domain/entity/customer";
 import { faker } from "@faker-js/faker";
 
 export default class CustomerFakeFactory {
diff --git a/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts b/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
index 7178ac2..9ce5ccd 100644
--- a/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
+++ b/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
@@ -1,9 +1,9 @@
-import CustomerRepo, { customerRepoKey } from "@/feature/customer/domain/i-repo/customer-repo";
+import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
 import { getMock } from "@/test/common/mock/mock-factory";
 import { describe } from "vitest";
 import { faker } from "@faker-js/faker";
 import CustomerFakeFactory from "@/test/common/fake-factory/customer/customer-fake-factory";
-import fetchCustomersUsecase from "@/feature/customer/domain/usecase/fetch-customers-usecase";
+import fetchCustomersUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-usecase";
 import mockDi from "@/test/common/mock/mock-di";
 /* -------------------------------------------------------------------------- */
 /*                                   Faking                                   */
-- 
2.39.5


From e592fef4ad99078757ba669ac9489656b2891e22 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 16:33:19 +0300
Subject: [PATCH 13/37] Add module for fetch summary info

---
 src/feature/common/server-di.ts               |  3 ++
 .../data/module/summary-info-di.ts            | 20 +++++++++++
 .../domain/summary-info-module-key.ts         |  1 +
 .../usecase/fetch-summary-info-usecase.ts     | 34 +++++++++++++++++++
 .../domain/value-object/summary-info.ts       | 17 ++++++++++
 5 files changed, 75 insertions(+)
 create mode 100644 src/feature/core/summary-info/data/module/summary-info-di.ts
 create mode 100644 src/feature/core/summary-info/domain/summary-info-module-key.ts
 create mode 100644 src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
 create mode 100644 src/feature/core/summary-info/domain/value-object/summary-info.ts

diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
index af68905..b1653f9 100644
--- a/src/feature/common/server-di.ts
+++ b/src/feature/common/server-di.ts
@@ -7,6 +7,8 @@ import getTestModule from "@/feature/infra/test/module/test-module";
 import getInvoiceDi from "@/feature/core/invoice/data/module/invoice-di";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 import { DependencyContainer } from "tsyringe";
+import { summaryInfoModuleKey } from "@/feature/core/summary-info/domain/summary-info-module-key";
+import getSummaryInfoDi from "@/feature/core/summary-info/data/module/summary-info-di";
 
 export default function serverDi(module: string): DependencyContainer {
     const getDi = {
@@ -14,6 +16,7 @@ export default function serverDi(module: string): DependencyContainer {
         [customerKey]: getCustomerDi,
         [customerInvoiceModuleKey]: getCustomerInvoiceDi,
         [invoiceModuleKey]: getInvoiceDi,
+        [summaryInfoModuleKey]: getSummaryInfoDi,
     }[module]
 
     if (!getDi) throw new Error("Server Di didn't found for module: " + module)
diff --git a/src/feature/core/summary-info/data/module/summary-info-di.ts b/src/feature/core/summary-info/data/module/summary-info-di.ts
new file mode 100644
index 0000000..cd181eb
--- /dev/null
+++ b/src/feature/core/summary-info/data/module/summary-info-di.ts
@@ -0,0 +1,20 @@
+import di from "@/bootstrap/di/init-di"
+import fetchCustomersAmountUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-amount-usecase"
+import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase"
+import fetchInvoicesStatusSummary from "@/feature/core/invoice/domain/usecase/fetch-invoices-status-summary"
+import fetchSummaryInfoUsecase from "@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase"
+
+export default function getSummaryInfoDi() {
+    const summaryInfoDi = di.createChildContainer()
+
+    summaryInfoDi.register(fetchAllInvoicesAmountUsecase.name, {
+        useValue: fetchAllInvoicesAmountUsecase
+    })
+    summaryInfoDi.register(fetchCustomersAmountUsecase.name, {
+        useValue: fetchCustomersAmountUsecase
+    })
+    summaryInfoDi.register(fetchInvoicesStatusSummary.name, {
+        useValue: fetchSummaryInfoUsecase
+    })
+    return summaryInfoDi
+}
\ No newline at end of file
diff --git a/src/feature/core/summary-info/domain/summary-info-module-key.ts b/src/feature/core/summary-info/domain/summary-info-module-key.ts
new file mode 100644
index 0000000..a2e027b
--- /dev/null
+++ b/src/feature/core/summary-info/domain/summary-info-module-key.ts
@@ -0,0 +1 @@
+export const summaryInfoModuleKey = "summaryInfoModuleKey"
\ No newline at end of file
diff --git a/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts b/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
new file mode 100644
index 0000000..e0a6305
--- /dev/null
+++ b/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
@@ -0,0 +1,34 @@
+import serverDi from "@/feature/common/server-di";
+import fetchCustomersAmountUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-amount-usecase";
+import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase";
+import fetchInvoicesStatusSummary from "@/feature/core/invoice/domain/usecase/fetch-invoices-status-summary";
+import { summaryInfoModuleKey } from "@/feature/core/summary-info/domain/summary-info-module-key";
+import SummaryInfo from "@/feature/core/summary-info/domain/value-object/summary-info";
+import { connection } from "next/server";
+
+export default async function fetchSummaryInfoUsecase(): Promise<SummaryInfo> {
+    connection()
+
+    try{
+        const summaryInfoDi = serverDi(summaryInfoModuleKey)
+        const invoicesAmountPromise = summaryInfoDi.resolve<typeof fetchAllInvoicesAmountUsecase>(fetchAllInvoicesAmountUsecase.name)()
+        const customersAmountPromise = summaryInfoDi.resolve<typeof fetchCustomersAmountUsecase>(fetchCustomersAmountUsecase.name)()
+        const invoiceSummaryPomise = summaryInfoDi.resolve<typeof fetchInvoicesStatusSummary>(fetchInvoicesStatusSummary.name)()
+
+        const [invoicesAmount, customersAmount, invoicesSummary] = await Promise.all([
+            invoicesAmountPromise,
+            customersAmountPromise,
+            invoiceSummaryPomise,
+        ]);
+
+
+        return new SummaryInfo({
+            invoicesNumber: invoicesAmount,
+            customersNumber: customersAmount,
+            invoicesSummary: invoicesSummary
+        })
+    } catch (error) {
+        console.error('Database Error:', error);
+        throw new Error('Failed to fetch card data.');
+    }
+}
\ No newline at end of file
diff --git a/src/feature/core/summary-info/domain/value-object/summary-info.ts b/src/feature/core/summary-info/domain/value-object/summary-info.ts
new file mode 100644
index 0000000..9c30901
--- /dev/null
+++ b/src/feature/core/summary-info/domain/value-object/summary-info.ts
@@ -0,0 +1,17 @@
+import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
+
+export default class SummaryInfo {
+    customersNumber: number;
+    invoicesNumber: number;
+    invoicesSummary: InvoiceStatusSummary 
+
+    constructor({
+        customersNumber,
+        invoicesNumber,
+        invoicesSummary
+    }: SummaryInfo) {
+        this.customersNumber = customersNumber
+        this.invoicesNumber = invoicesNumber
+        this.invoicesSummary = invoicesSummary
+    }
+}
\ No newline at end of file
-- 
2.39.5


From 8b79d9aa4b014341d5fe0755f024a8082eac870a Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 16:57:12 +0300
Subject: [PATCH 14/37] Add fetch revenues module

---
 src/app/lib/data.ts                           | 73 -------------------
 src/app/lib/definitions.ts                    | 26 -------
 src/app/lib/utils.ts                          | 49 +------------
 src/feature/common/server-di.ts               |  3 +
 .../core/revenue/data/module/revenue-di.ts    | 10 +++
 .../core/revenue/data/repo/revenue-db-repo.ts | 43 +++++++++++
 .../core/revenue/domain/entity/revenue.ts     | 14 ++++
 .../revenue/domain/i-repo/revenue-repo.ts     |  7 ++
 .../core/revenue/domain/revenue-module-key.ts |  1 +
 .../domain/usecase/fetch-revenues-usecase.ts  |  9 +++
 10 files changed, 88 insertions(+), 147 deletions(-)
 delete mode 100644 src/app/lib/data.ts
 delete mode 100644 src/app/lib/definitions.ts
 create mode 100644 src/feature/core/revenue/data/module/revenue-di.ts
 create mode 100644 src/feature/core/revenue/data/repo/revenue-db-repo.ts
 create mode 100644 src/feature/core/revenue/domain/entity/revenue.ts
 create mode 100644 src/feature/core/revenue/domain/i-repo/revenue-repo.ts
 create mode 100644 src/feature/core/revenue/domain/revenue-module-key.ts
 create mode 100644 src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts

diff --git a/src/app/lib/data.ts b/src/app/lib/data.ts
deleted file mode 100644
index 6326383..0000000
--- a/src/app/lib/data.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { sql } from '@/bootstrap/db/db';
-import {
-  Revenue,
-  Invoice,
-  Customer,
-} from './definitions';
-import { formatCurrency } from './utils';
-import postgres from 'postgres';
-import { connection } from 'next/server';
-
-export async function fetchRevenue() {
-  // This is equivalent to in fetch(..., {cache: 'no-store'}).
-  connection()
-
-  try {
-    // Artificially delay a response for demo purposes.
-    // Don't do this in production :)
-
-    console.log('Fetching revenue data...');
-    await new Promise((resolve) => setTimeout(resolve, 3000));
-
-    const data = await sql`SELECT * FROM revenue`;
-
-    console.log('Data fetch completed after 3 seconds.');
-
-    return data as postgres.RowList<Revenue[]>;
-  } catch (error) {
-    console.error('Database Error:', error);
-    throw new Error('Failed to fetch revenue data.');
-  }
-}
-
-export async function fetchCardData() {
-    // This is equivalent to in fetch(..., {cache: 'no-store'}).
-  connection()
-
-  try {
-    // You can probably combine these into a single SQL query
-    // However, we are intentionally splitting them to demonstrate
-    // how to initialize multiple queries in parallel with JS.
-    const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
-    const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
-    const invoiceStatusPromise = sql`SELECT
-         id,
-         SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
-         SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
-         FROM invoices`;
-
-    const data = await Promise.all([
-      invoiceCountPromise,
-      customerCountPromise,
-      invoiceStatusPromise,
-    ]);
-
-    const invoices = data[0] as postgres.RowList<Invoice[]>
-    const customres = data[1] as postgres.RowList<Customer[]>
-    const invoiceStatus = data[2] as postgres.RowList<({id: string; paid: string, pending: string})[]>
-    const numberOfInvoices = Number(invoices.count ?? '0');
-    const numberOfCustomers = Number(customres.count ?? '0');
-    const totalPaidInvoices = formatCurrency(Number(invoiceStatus.at(0)?.paid ?? '0'));
-    const totalPendingInvoices = formatCurrency(Number(invoiceStatus.at(0)?.pending ?? '0'));
-
-    return {
-      numberOfCustomers,
-      numberOfInvoices,
-      totalPaidInvoices,
-      totalPendingInvoices,
-    };
-  } catch (error) {
-    console.error('Database Error:', error);
-    throw new Error('Failed to fetch card data.');
-  }
-}
diff --git a/src/app/lib/definitions.ts b/src/app/lib/definitions.ts
deleted file mode 100644
index b26dd32..0000000
--- a/src/app/lib/definitions.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-export type Customer = {
-  id: string;
-  name: string;
-  email: string;
-  image_url: string;
-};
-
-export type Invoice = {
-  id: string; // Will be created on the database
-  customer_id: string;
-  amount: number; // Stored in cents
-  status: 'pending' | 'paid';
-  date: string;
-};
-
-export type Revenue = {
-  month: string;
-  revenue: number;
-};
-
-export type InvoiceForm = {
-  id: string;
-  customer_id: string;
-  amount: number;
-  status: 'pending' | 'paid';
-};
diff --git a/src/app/lib/utils.ts b/src/app/lib/utils.ts
index b7f7cff..c3184af 100644
--- a/src/app/lib/utils.ts
+++ b/src/app/lib/utils.ts
@@ -1,4 +1,4 @@
-import { Revenue } from './definitions';
+import Revenue from "@/feature/core/revenue/domain/entity/revenue";
 
 export const formatCurrency = (amount: number) => {
   return (amount / 100).toLocaleString('en-US', {
@@ -7,20 +7,6 @@ export const formatCurrency = (amount: number) => {
   });
 };
 
-export const formatDateToLocal = (
-  dateStr: string,
-  locale: string = 'en-US',
-) => {
-  const date = new Date(dateStr);
-  const options: Intl.DateTimeFormatOptions = {
-    day: 'numeric',
-    month: 'short',
-    year: 'numeric',
-  };
-  const formatter = new Intl.DateTimeFormat(locale, options);
-  return formatter.format(date);
-};
-
 export const generateYAxis = (revenue: Revenue[]) => {
   // Calculate what labels we need to display on the y-axis
   // based on highest record and in 1000s
@@ -34,36 +20,3 @@ export const generateYAxis = (revenue: Revenue[]) => {
 
   return { yAxisLabels, topLabel };
 };
-
-export const generatePagination = (currentPage: number, totalPages: number) => {
-  // If the total number of pages is 7 or less,
-  // display all pages without any ellipsis.
-  if (totalPages <= 7) {
-    return Array.from({ length: totalPages }, (_, i) => i + 1);
-  }
-
-  // If the current page is among the first 3 pages,
-  // show the first 3, an ellipsis, and the last 2 pages.
-  if (currentPage <= 3) {
-    return [1, 2, 3, '...', totalPages - 1, totalPages];
-  }
-
-  // If the current page is among the last 3 pages,
-  // show the first 2, an ellipsis, and the last 3 pages.
-  if (currentPage >= totalPages - 2) {
-    return [1, 2, '...', totalPages - 2, totalPages - 1, totalPages];
-  }
-
-  // If the current page is somewhere in the middle,
-  // show the first page, an ellipsis, the current page and its neighbors,
-  // another ellipsis, and the last page.
-  return [
-    1,
-    '...',
-    currentPage - 1,
-    currentPage,
-    currentPage + 1,
-    '...',
-    totalPages,
-  ];
-};
diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
index b1653f9..7ce0735 100644
--- a/src/feature/common/server-di.ts
+++ b/src/feature/common/server-di.ts
@@ -9,6 +9,8 @@ import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 import { DependencyContainer } from "tsyringe";
 import { summaryInfoModuleKey } from "@/feature/core/summary-info/domain/summary-info-module-key";
 import getSummaryInfoDi from "@/feature/core/summary-info/data/module/summary-info-di";
+import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
+import getRevenueDi from "@/feature/core/revenue/data/module/revenue-di";
 
 export default function serverDi(module: string): DependencyContainer {
     const getDi = {
@@ -17,6 +19,7 @@ export default function serverDi(module: string): DependencyContainer {
         [customerInvoiceModuleKey]: getCustomerInvoiceDi,
         [invoiceModuleKey]: getInvoiceDi,
         [summaryInfoModuleKey]: getSummaryInfoDi,
+        [revenueModuleKey]: getRevenueDi,
     }[module]
 
     if (!getDi) throw new Error("Server Di didn't found for module: " + module)
diff --git a/src/feature/core/revenue/data/module/revenue-di.ts b/src/feature/core/revenue/data/module/revenue-di.ts
new file mode 100644
index 0000000..921a2ed
--- /dev/null
+++ b/src/feature/core/revenue/data/module/revenue-di.ts
@@ -0,0 +1,10 @@
+import di from "@/bootstrap/di/init-di"
+import RevenueDbRepo from "@/feature/core/revenue/data/repo/revenue-db-repo"
+import { revenueRepoKey } from "@/feature/core/revenue/domain/i-repo/revenue-repo"
+
+export default function getRevenueDi() {
+    const revenueDi = di.createChildContainer()
+
+    revenueDi.register(revenueRepoKey, RevenueDbRepo)
+    return revenueDi
+}
\ No newline at end of file
diff --git a/src/feature/core/revenue/data/repo/revenue-db-repo.ts b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
new file mode 100644
index 0000000..b29c5d2
--- /dev/null
+++ b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
@@ -0,0 +1,43 @@
+import { sql } from "@/bootstrap/db/db";
+import Revenue from "@/feature/core/revenue/domain/entity/revenue";
+import RevenueRepo from "@/feature/core/revenue/domain/i-repo/revenue-repo";
+import { connection } from "next/server";
+import postgres from "postgres";
+
+export type RevenueDbResponse = {
+  month: string;
+  revenue: number;
+};
+export default class RevenueDbRepo implements RevenueRepo {
+    async fetchRevenues(): Promise<Revenue[]> {
+        // This is equivalent to in fetch(..., {cache: 'no-store'}).
+        connection()
+        try {
+            // Artificially delay a response for demo purposes.
+            // Don't do this in production :)
+            await new Promise((resolve) => setTimeout(resolve, 3000));
+
+            const data = await sql`SELECT * FROM revenue` as postgres.RowList<RevenueDbResponse[]>;
+
+            console.log('Data fetch completed after 3 seconds.');
+
+            return this.revenuesDto(data);
+        } catch (error) {
+            console.error('Database Error:', error);
+            throw new Error('Failed to fetch revenue data.');
+        }
+    }
+
+
+    private revenuesDto(dbResponse: RevenueDbResponse[]): Revenue[] {
+        return dbResponse.map((dbRevenue) => this.revenueDto(dbRevenue))
+    }
+    
+    private revenueDto(dbResponse: RevenueDbResponse): Revenue {
+        return new Revenue({
+            month: dbResponse.month,
+            revenue: dbResponse.revenue
+        })
+    }
+    
+}
\ No newline at end of file
diff --git a/src/feature/core/revenue/domain/entity/revenue.ts b/src/feature/core/revenue/domain/entity/revenue.ts
new file mode 100644
index 0000000..290ca32
--- /dev/null
+++ b/src/feature/core/revenue/domain/entity/revenue.ts
@@ -0,0 +1,14 @@
+export default class Revenue {
+    month: string;
+    revenue: number;
+
+    constructor(
+        {
+            month,
+            revenue
+        }: Revenue
+    ) {
+        this.month = month
+        this.revenue = revenue
+    }
+}
\ No newline at end of file
diff --git a/src/feature/core/revenue/domain/i-repo/revenue-repo.ts b/src/feature/core/revenue/domain/i-repo/revenue-repo.ts
new file mode 100644
index 0000000..be6924b
--- /dev/null
+++ b/src/feature/core/revenue/domain/i-repo/revenue-repo.ts
@@ -0,0 +1,7 @@
+import Revenue from "@/feature/core/revenue/domain/entity/revenue";
+
+export default interface RevenueRepo {
+    fetchRevenues(): Promise<Revenue[]>
+}
+
+export const revenueRepoKey = "revenueRepoKey"
\ No newline at end of file
diff --git a/src/feature/core/revenue/domain/revenue-module-key.ts b/src/feature/core/revenue/domain/revenue-module-key.ts
new file mode 100644
index 0000000..27150fb
--- /dev/null
+++ b/src/feature/core/revenue/domain/revenue-module-key.ts
@@ -0,0 +1 @@
+export const revenueModuleKey = "RevenueModuleKey"
\ No newline at end of file
diff --git a/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
new file mode 100644
index 0000000..c9e6068
--- /dev/null
+++ b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
@@ -0,0 +1,9 @@
+import serverDi from "@/feature/common/server-di";
+import Revenue from "@/feature/core/revenue/domain/entity/revenue";
+import RevenueRepo from "@/feature/core/revenue/domain/i-repo/revenue-repo";
+import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
+
+export default function fetchRevenuesUsecase(): Promise<Revenue[]> {
+    const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueModuleKey)
+    return repo.fetchRevenues()
+}
\ No newline at end of file
-- 
2.39.5


From 43a622851da498af2c202f70449156a1101fc9f1 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 17:50:43 +0300
Subject: [PATCH 15/37] Update RFCs to use feature layer architecture

---
 .gitignore                                    |  2 +-
 src/app/dashboard/components/cards.tsx        | 17 +++++-------
 .../dashboard/components/latest-invoices.tsx  | 14 +++++-----
 .../dashboard/components/revenue-chart.tsx    |  4 +--
 .../dashboard/module/dashboard-app-module.ts  | 27 +++++++++++++++++++
 src/feature/common/server-di.ts               |  9 +++++--
 .../data/module/customer-invoice-di.ts        |  9 ++-----
 .../data/repo/customer-invoice-db-repo.ts     |  6 +----
 .../fetch-customer-invoices-usecase.ts        |  5 ++--
 .../core/customer/data/module/customer-di.ts  |  5 ----
 .../usecase/fetch-customers-amount-usecase.ts |  2 +-
 .../domain/usecase/fetch-customers-usecase.ts |  2 +-
 .../fetch-all-invoices-amount-usecase.ts      |  2 +-
 .../usecase/fetch-invoices-status-summary.ts  |  2 +-
 .../core/revenue/data/repo/revenue-db-repo.ts |  1 -
 .../domain/usecase/fetch-revenues-usecase.ts  |  6 ++---
 .../data/module/summary-info-di.ts            |  3 +--
 17 files changed, 64 insertions(+), 52 deletions(-)
 create mode 100644 src/app/dashboard/module/dashboard-app-module.ts

diff --git a/.gitignore b/.gitignore
index 26b002a..39c19d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,7 @@
 !.yarn/plugins
 !.yarn/releases
 !.yarn/versions
-
+.vscode
 # testing
 /coverage
 
diff --git a/src/app/dashboard/components/cards.tsx b/src/app/dashboard/components/cards.tsx
index 72bb37b..5060f9f 100644
--- a/src/app/dashboard/components/cards.tsx
+++ b/src/app/dashboard/components/cards.tsx
@@ -1,10 +1,10 @@
+import fetchSummaryInfoUsecase from '@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase';
 import {
   BanknotesIcon,
   ClockIcon,
   UserGroupIcon,
   InboxIcon,
 } from '@heroicons/react/24/outline';
-import { fetchCardData } from '@/app/lib/data';
 
 const iconMap = {
   collected: BanknotesIcon,
@@ -14,21 +14,16 @@ const iconMap = {
 };
 
 export default async function CardWrapper() {
-   const {
-    numberOfInvoices,
-    numberOfCustomers,
-    totalPaidInvoices,
-    totalPendingInvoices,
-  } = await fetchCardData();
+   const {customersNumber, invoicesNumber, invoicesSummary } = await fetchSummaryInfoUsecase();
   
   return (
     <>
-      <Card title="Collected" value={totalPaidInvoices} type="collected" />
-      <Card title="Pending" value={totalPendingInvoices} type="pending" />
-      <Card title="Total Invoices" value={numberOfInvoices} type="invoices" />
+      <Card title="Collected" value={invoicesSummary.paid} type="collected" />
+      <Card title="Pending" value={invoicesSummary.pending} type="pending" />
+      <Card title="Total Invoices" value={invoicesNumber} type="invoices" />
       <Card
         title="Total Customers"
-        value={numberOfCustomers}
+        value={customersNumber}
         type="customers"
       />
     </>
diff --git a/src/app/dashboard/components/latest-invoices.tsx b/src/app/dashboard/components/latest-invoices.tsx
index 5c7cd95..6f62655 100644
--- a/src/app/dashboard/components/latest-invoices.tsx
+++ b/src/app/dashboard/components/latest-invoices.tsx
@@ -1,9 +1,9 @@
+import fetchCustomerInvoicesUsecase from '@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase';
 import { ArrowPathIcon } from '@heroicons/react/24/outline';
 import clsx from 'clsx';
 import Image from 'next/image';
-import { fetchLatestInvoices } from '@/app/lib/data';
 export default async function LatestInvoices() {
-  const latestInvoices = await fetchLatestInvoices();
+  const latestInvoices = await fetchCustomerInvoicesUsecase();
 
   return (
     <div className="flex w-full flex-col md:col-span-4">
@@ -26,25 +26,25 @@ export default async function LatestInvoices() {
               >
                 <div className="flex items-center">
                   <Image
-                    src={invoice.image_url}
-                    alt={`${invoice.name}'s profile picture`}
+                    src={invoice.customerImageUrl}
+                    alt={`${invoice.customerName}'s profile picture`}
                     className="mr-4 rounded-full"
                     width={32}
                     height={32}
                   />
                   <div className="min-w-0">
                     <p className="truncate text-sm font-semibold md:text-base">
-                      {invoice.name}
+                      {invoice.customerName}
                     </p>
                     <p className="hidden text-sm text-gray-500 sm:block">
-                      {invoice.email}
+                      {invoice.customerEmail}
                     </p>
                   </div>
                 </div>
                 <p
                   className="truncate text-sm font-medium md:text-base"
                 >
-                  {invoice.amount}
+                  {invoice.invoicesAmount}
                 </p>
               </div>
             );
diff --git a/src/app/dashboard/components/revenue-chart.tsx b/src/app/dashboard/components/revenue-chart.tsx
index 49d888c..f6a6f91 100644
--- a/src/app/dashboard/components/revenue-chart.tsx
+++ b/src/app/dashboard/components/revenue-chart.tsx
@@ -1,9 +1,9 @@
 import { generateYAxis } from '@/app/lib/utils';
+import fetchRevenuesUsecase from '@/feature/core/revenue/domain/usecase/fetch-revenues-usecase';
 import { CalendarIcon } from '@heroicons/react/24/outline';
-import { fetchRevenue } from '@/app/lib/data';
 
 export default async function RevenueChart() {
-  const revenue = await fetchRevenue();
+  const revenue = await fetchRevenuesUsecase();
 
   const chartHeight = 350;
 
diff --git a/src/app/dashboard/module/dashboard-app-module.ts b/src/app/dashboard/module/dashboard-app-module.ts
new file mode 100644
index 0000000..fd9f144
--- /dev/null
+++ b/src/app/dashboard/module/dashboard-app-module.ts
@@ -0,0 +1,27 @@
+import di from "@/bootstrap/di/init-di"
+import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
+import fetchCustomersUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-usecase";
+import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase";
+import fetchRevenuesUsecase from "@/feature/core/revenue/domain/usecase/fetch-revenues-usecase";
+
+export default function dashboardAppModule() {
+    const dashboardDi = di.createChildContainer()
+    
+    dashboardDi.register(fetchCustomersUsecase.name, {
+        useValue: fetchCustomersUsecase
+    })
+
+    dashboardDi.register(fetchAllInvoicesAmountUsecase.name, {
+        useValue: fetchAllInvoicesAmountUsecase
+    })
+    dashboardDi.register(fetchAllInvoicesAmountUsecase.name, {
+        useValue: fetchAllInvoicesAmountUsecase
+    })
+    dashboardDi.register(fetchCustomerInvoicesUsecase.name, {
+        useValue: fetchCustomerInvoicesUsecase
+    })
+    dashboardDi.register(fetchRevenuesUsecase.name, {
+        useValue: fetchRevenuesUsecase
+    })
+    return dashboardDi
+}
diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
index 7ce0735..401e706 100644
--- a/src/feature/common/server-di.ts
+++ b/src/feature/common/server-di.ts
@@ -12,7 +12,10 @@ import getSummaryInfoDi from "@/feature/core/summary-info/data/module/summary-in
 import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
 import getRevenueDi from "@/feature/core/revenue/data/module/revenue-di";
 
+const memoizedDis: Record<string, DependencyContainer> = {}
+
 export default function serverDi(module: string): DependencyContainer {
+    if (memoizedDis[module]) return memoizedDis[module]
     const getDi = {
         [testModuleKey]: getTestModule,
         [customerKey]: getCustomerDi,
@@ -23,6 +26,8 @@ export default function serverDi(module: string): DependencyContainer {
     }[module]
 
     if (!getDi) throw new Error("Server Di didn't found for module: " + module)
-
-    return getDi()
+    
+    const di = getDi()
+    memoizedDis[module] = di 
+    return di
 }
\ No newline at end of file
diff --git a/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts b/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
index 76a7453..5364a92 100644
--- a/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
+++ b/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
@@ -1,16 +1,11 @@
 import di from "@/bootstrap/di/init-di";
+import CustomerInvoiceDbRepo from "@/feature/core/customer-invoice/data/repo/customer-invoice-db-repo";
 import { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
-import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
-import CustomerDbRepo from "@/feature/core/customer/data/repo/customer-db-repo";
 import { DependencyContainer } from "tsyringe";
 
 export default function getCustomerInvoiceDi(): DependencyContainer {
     const customerInvoiceDi = di.createChildContainer()
 
-    customerInvoiceDi.register(fetchCustomerInvoicesUsecase.name, {
-        useValue: fetchCustomerInvoicesUsecase
-    })
-
-    customerInvoiceDi.register(customerInvoiceRepoKey, CustomerDbRepo)
+    customerInvoiceDi.register(customerInvoiceRepoKey, CustomerInvoiceDbRepo)
     return customerInvoiceDi
 }
\ No newline at end of file
diff --git a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
index 45ec6ab..18f7db1 100644
--- a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
+++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -2,7 +2,6 @@ import { formatCurrency } from "@/app/lib/utils";
 import { sql } from "@/bootstrap/db/db";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 import CustomerInvoiceRepo from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
-import { connection } from "next/server";
 import postgres from "postgres";
 
 type customerInvoiceDbResponse = {
@@ -13,11 +12,8 @@ type customerInvoiceDbResponse = {
   amount: string;
 }
 
-export default class CustomerDbRepo implements CustomerInvoiceRepo {
+export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
     async fetchList(): Promise<CustomerInvoice[]> {
-        // This is equivalent to in fetch(..., {cache: 'no-store'}).
-        connection()
-
         try {
             const data = await sql`
             SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
diff --git a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
index fd97f46..5947553 100644
--- a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
+++ b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
@@ -3,9 +3,10 @@ import serverDi from "@/feature/common/server-di";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
 import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
+import { connection } from "next/server";
 
-export default function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> {
+export default async function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> {
+    connection()
     const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey)
-
     return repo.fetchList()
 }
\ No newline at end of file
diff --git a/src/feature/core/customer/data/module/customer-di.ts b/src/feature/core/customer/data/module/customer-di.ts
index 1fa4398..c3806ee 100644
--- a/src/feature/core/customer/data/module/customer-di.ts
+++ b/src/feature/core/customer/data/module/customer-di.ts
@@ -1,16 +1,11 @@
 import di from "@/bootstrap/di/init-di";
 import CustomerDbRepo from "@/feature/core/customer/data/repo/customer-db-repo";
 import { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
-import fetchCustomersUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-usecase";
 import { DependencyContainer } from "tsyringe";
 
 export default function getCustomerDi(): DependencyContainer {
     const customerDi = di.createChildContainer()
 
-    customerDi.register(fetchCustomersUsecase.name, {
-        useValue: fetchCustomersUsecase
-    })
-
     customerDi.register(customerRepoKey, CustomerDbRepo)
     return customerDi
 }
\ No newline at end of file
diff --git a/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
index f90d5c3..71cd13d 100644
--- a/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
@@ -2,7 +2,7 @@ import serverDi from "@/feature/common/server-di";
 import { customerKey } from "@/feature/core/customer/customer-key";
 import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
 
-export default function fetchCustomersAmountUsecase(): Promise<number> {
+export default async function fetchCustomersAmountUsecase(): Promise<number> {
     const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
     return repo.fetchCustomersAmount()
 }
\ No newline at end of file
diff --git a/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
index 60bd0c5..d28584d 100644
--- a/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
@@ -5,7 +5,7 @@ import { customerKey } from "@/feature/core/customer/customer-key";
 import Customer from "@/feature/core/customer/domain/entity/customer";
 import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
 
-export default function fetchCustomersUsecase(query: string): Promise<Customer[]> {
+export default async function fetchCustomersUsecase(query: string): Promise<Customer[]> {
     const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
 
     return repo.fetchList(query)
diff --git a/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts b/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
index 93e2deb..48840f4 100644
--- a/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
+++ b/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
@@ -3,7 +3,7 @@ import serverDi from "@/feature/common/server-di";
 import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 
-export default function fetchAllInvoicesAmountUsecase(): Promise<number> {
+export default async function fetchAllInvoicesAmountUsecase(): Promise<number> {
     const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
 
     return repo.fetchAllInvoicesAmount()
diff --git a/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts b/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
index 07c76a8..3bb9a5e 100644
--- a/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
+++ b/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
@@ -3,7 +3,7 @@ import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-rep
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 
-export default function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
+export default async function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
     const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
     return repo.fetchInvoicesStatusSummary()
 }
\ No newline at end of file
diff --git a/src/feature/core/revenue/data/repo/revenue-db-repo.ts b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
index b29c5d2..7adc439 100644
--- a/src/feature/core/revenue/data/repo/revenue-db-repo.ts
+++ b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
@@ -11,7 +11,6 @@ export type RevenueDbResponse = {
 export default class RevenueDbRepo implements RevenueRepo {
     async fetchRevenues(): Promise<Revenue[]> {
         // This is equivalent to in fetch(..., {cache: 'no-store'}).
-        connection()
         try {
             // Artificially delay a response for demo purposes.
             // Don't do this in production :)
diff --git a/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
index c9e6068..0f428c5 100644
--- a/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
+++ b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
@@ -1,9 +1,9 @@
 import serverDi from "@/feature/common/server-di";
 import Revenue from "@/feature/core/revenue/domain/entity/revenue";
-import RevenueRepo from "@/feature/core/revenue/domain/i-repo/revenue-repo";
+import RevenueRepo, { revenueRepoKey } from "@/feature/core/revenue/domain/i-repo/revenue-repo";
 import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
 
-export default function fetchRevenuesUsecase(): Promise<Revenue[]> {
-    const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueModuleKey)
+export default async function fetchRevenuesUsecase(): Promise<Revenue[]> {
+    const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueRepoKey)
     return repo.fetchRevenues()
 }
\ No newline at end of file
diff --git a/src/feature/core/summary-info/data/module/summary-info-di.ts b/src/feature/core/summary-info/data/module/summary-info-di.ts
index cd181eb..9172b3d 100644
--- a/src/feature/core/summary-info/data/module/summary-info-di.ts
+++ b/src/feature/core/summary-info/data/module/summary-info-di.ts
@@ -2,7 +2,6 @@ import di from "@/bootstrap/di/init-di"
 import fetchCustomersAmountUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-amount-usecase"
 import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase"
 import fetchInvoicesStatusSummary from "@/feature/core/invoice/domain/usecase/fetch-invoices-status-summary"
-import fetchSummaryInfoUsecase from "@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase"
 
 export default function getSummaryInfoDi() {
     const summaryInfoDi = di.createChildContainer()
@@ -14,7 +13,7 @@ export default function getSummaryInfoDi() {
         useValue: fetchCustomersAmountUsecase
     })
     summaryInfoDi.register(fetchInvoicesStatusSummary.name, {
-        useValue: fetchSummaryInfoUsecase
+        useValue: fetchInvoicesStatusSummary 
     })
     return summaryInfoDi
 }
\ No newline at end of file
-- 
2.39.5


From 0cb29f0b5f100c8b896d17095d2755bfe5da5576 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 18:03:11 +0300
Subject: [PATCH 16/37] Move caching decision to the usecase layer

---
 src/feature/core/customer/data/repo/customer-db-repo.ts         | 2 --
 .../core/customer/domain/usecase/fetch-customers-usecase.ts     | 2 ++
 src/feature/core/revenue/data/repo/revenue-db-repo.ts           | 1 -
 3 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/feature/core/customer/data/repo/customer-db-repo.ts b/src/feature/core/customer/data/repo/customer-db-repo.ts
index 82c0494..ad6705f 100644
--- a/src/feature/core/customer/data/repo/customer-db-repo.ts
+++ b/src/feature/core/customer/data/repo/customer-db-repo.ts
@@ -17,8 +17,6 @@ type customerDbResponse = {
 
 export default class CustomerDbRepo implements CustomerRepo {
     async fetchList(query: string): Promise<Customer[]> {
-        // This is equivalent to in fetch(..., {cache: 'no-store'}).
-        connection()
         try {
             const data = await sql`
                 SELECT
diff --git a/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
index d28584d..c83a252 100644
--- a/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
@@ -4,8 +4,10 @@ import serverDi from "@/feature/common/server-di";
 import { customerKey } from "@/feature/core/customer/customer-key";
 import Customer from "@/feature/core/customer/domain/entity/customer";
 import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
+import { connection } from "next/server";
 
 export default async function fetchCustomersUsecase(query: string): Promise<Customer[]> {
+    connection()
     const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
 
     return repo.fetchList(query)
diff --git a/src/feature/core/revenue/data/repo/revenue-db-repo.ts b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
index 7adc439..92ac993 100644
--- a/src/feature/core/revenue/data/repo/revenue-db-repo.ts
+++ b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
@@ -10,7 +10,6 @@ export type RevenueDbResponse = {
 };
 export default class RevenueDbRepo implements RevenueRepo {
     async fetchRevenues(): Promise<Revenue[]> {
-        // This is equivalent to in fetch(..., {cache: 'no-store'}).
         try {
             // Artificially delay a response for demo purposes.
             // Don't do this in production :)
-- 
2.39.5


From d358d4cd035773cfa7227e80c4cf38a01c17750e Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 18:14:29 +0300
Subject: [PATCH 17/37] Update card wrapper to follow controller based
 architecture

---
 src/app/dashboard/(overview)/page.tsx         |   2 +-
 .../components/card/card-controller.ts        |  19 ++
 src/app/dashboard/components/card/card.tsx    |  29 +++
 src/app/dashboard/components/cards.tsx        |  57 ------
 .../components/cards/cards-controller.ts      |   5 +
 src/app/dashboard/components/cards/cards.tsx  |  20 ++
 src/app/lib/placeholder-data.js               | 188 ------------------
 src/app/lib/utils.ts                          |   6 -
 src/bootstrap/helpers/global-helpers.ts       |   0
 src/feature/common/feature-helpers.ts         |   6 +
 .../data/repo/customer-invoice-db-repo.ts     |   2 +-
 .../customer/data/repo/customer-db-repo.ts    |   3 +-
 .../core/invoice/data/repo/invoice-db-repo.ts |   2 +-
 13 files changed, 83 insertions(+), 256 deletions(-)
 create mode 100644 src/app/dashboard/components/card/card-controller.ts
 create mode 100644 src/app/dashboard/components/card/card.tsx
 delete mode 100644 src/app/dashboard/components/cards.tsx
 create mode 100644 src/app/dashboard/components/cards/cards-controller.ts
 create mode 100644 src/app/dashboard/components/cards/cards.tsx
 delete mode 100644 src/app/lib/placeholder-data.js
 create mode 100644 src/bootstrap/helpers/global-helpers.ts
 create mode 100644 src/feature/common/feature-helpers.ts

diff --git a/src/app/dashboard/(overview)/page.tsx b/src/app/dashboard/(overview)/page.tsx
index 6d632b5..463edbb 100644
--- a/src/app/dashboard/(overview)/page.tsx
+++ b/src/app/dashboard/(overview)/page.tsx
@@ -1,5 +1,5 @@
 import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/components/skeleton/skeletons";
-import CardWrapper from "@/app/dashboard/components/cards";
+import CardWrapper from "@/app/dashboard/components/cards/cards";
 import LatestInvoices from "@/app/dashboard/components/latest-invoices";
 import RevenueChart from "@/app/dashboard/components/revenue-chart";
 import { Suspense } from "react";
diff --git a/src/app/dashboard/components/card/card-controller.ts b/src/app/dashboard/components/card/card-controller.ts
new file mode 100644
index 0000000..43c40f7
--- /dev/null
+++ b/src/app/dashboard/components/card/card-controller.ts
@@ -0,0 +1,19 @@
+import {
+  BanknotesIcon,
+  ClockIcon,
+  UserGroupIcon,
+  InboxIcon,
+} from '@heroicons/react/24/outline';
+
+export default function cardController(props: {  type: 'invoices' | 'customers' | 'pending' | 'collected'; }) {
+    const { type } = props
+    const iconMap = {
+        collected: BanknotesIcon,
+        customers: UserGroupIcon,
+        pending: ClockIcon,
+        invoices: InboxIcon,
+    };
+    return {
+        Icon: iconMap[type]
+    }
+}
\ No newline at end of file
diff --git a/src/app/dashboard/components/card/card.tsx b/src/app/dashboard/components/card/card.tsx
new file mode 100644
index 0000000..5494f4f
--- /dev/null
+++ b/src/app/dashboard/components/card/card.tsx
@@ -0,0 +1,29 @@
+import cardController from "@/app/dashboard/components/card/card-controller";
+
+
+
+export function Card({
+  title,
+  value,
+  type,
+}: {
+  title: string;
+  value: number | string;
+  type: 'invoices' | 'customers' | 'pending' | 'collected';
+}) {
+ const { Icon } = cardController({type})
+
+  return (
+    <div className="rounded-xl bg-gray-50 p-2 shadow-sm">
+      <div className="flex p-4">
+        {Icon ? <Icon className="h-5 w-5 text-gray-700" /> : null}
+        <h3 className="ml-2 text-sm font-medium">{title}</h3>
+      </div>
+      <p
+        className="rounded-xl bg-white px-4 py-8 text-center text-2xl"
+      >
+        {value}
+      </p>
+    </div>
+  );
+}
\ No newline at end of file
diff --git a/src/app/dashboard/components/cards.tsx b/src/app/dashboard/components/cards.tsx
deleted file mode 100644
index 5060f9f..0000000
--- a/src/app/dashboard/components/cards.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import fetchSummaryInfoUsecase from '@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase';
-import {
-  BanknotesIcon,
-  ClockIcon,
-  UserGroupIcon,
-  InboxIcon,
-} from '@heroicons/react/24/outline';
-
-const iconMap = {
-  collected: BanknotesIcon,
-  customers: UserGroupIcon,
-  pending: ClockIcon,
-  invoices: InboxIcon,
-};
-
-export default async function CardWrapper() {
-   const {customersNumber, invoicesNumber, invoicesSummary } = await fetchSummaryInfoUsecase();
-  
-  return (
-    <>
-      <Card title="Collected" value={invoicesSummary.paid} type="collected" />
-      <Card title="Pending" value={invoicesSummary.pending} type="pending" />
-      <Card title="Total Invoices" value={invoicesNumber} type="invoices" />
-      <Card
-        title="Total Customers"
-        value={customersNumber}
-        type="customers"
-      />
-    </>
-  );
-}
-
-export function Card({
-  title,
-  value,
-  type,
-}: {
-  title: string;
-  value: number | string;
-  type: 'invoices' | 'customers' | 'pending' | 'collected';
-}) {
-  const Icon = iconMap[type];
-
-  return (
-    <div className="rounded-xl bg-gray-50 p-2 shadow-sm">
-      <div className="flex p-4">
-        {Icon ? <Icon className="h-5 w-5 text-gray-700" /> : null}
-        <h3 className="ml-2 text-sm font-medium">{title}</h3>
-      </div>
-      <p
-        className="rounded-xl bg-white px-4 py-8 text-center text-2xl"
-      >
-        {value}
-      </p>
-    </div>
-  );
-}
diff --git a/src/app/dashboard/components/cards/cards-controller.ts b/src/app/dashboard/components/cards/cards-controller.ts
new file mode 100644
index 0000000..b8aaf19
--- /dev/null
+++ b/src/app/dashboard/components/cards/cards-controller.ts
@@ -0,0 +1,5 @@
+import fetchSummaryInfoUsecase from "@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase";
+
+export default function cardsController() {
+   return fetchSummaryInfoUsecase();
+}
\ No newline at end of file
diff --git a/src/app/dashboard/components/cards/cards.tsx b/src/app/dashboard/components/cards/cards.tsx
new file mode 100644
index 0000000..c39a4fe
--- /dev/null
+++ b/src/app/dashboard/components/cards/cards.tsx
@@ -0,0 +1,20 @@
+import { Card } from '@/app/dashboard/components/card/card';
+import cardsController from '@/app/dashboard/components/cards/cards-controller';
+
+
+export default async function CardWrapper() {
+  const {customersNumber, invoicesNumber, invoicesSummary } = await cardsController();
+  
+  return (
+    <>
+      <Card title="Collected" value={invoicesSummary.paid} type="collected" />
+      <Card title="Pending" value={invoicesSummary.pending} type="pending" />
+      <Card title="Total Invoices" value={invoicesNumber} type="invoices" />
+      <Card
+        title="Total Customers"
+        value={customersNumber}
+        type="customers"
+      />
+    </>
+  );
+}
\ No newline at end of file
diff --git a/src/app/lib/placeholder-data.js b/src/app/lib/placeholder-data.js
deleted file mode 100644
index 15a4156..0000000
--- a/src/app/lib/placeholder-data.js
+++ /dev/null
@@ -1,188 +0,0 @@
-// This file contains placeholder data that you'll be replacing with real data in the Data Fetching chapter:
-// https://nextjs.org/learn/dashboard-app/fetching-data
-const users = [
-  {
-    id: '410544b2-4001-4271-9855-fec4b6a6442a',
-    name: 'User',
-    email: 'user@nextmail.com',
-    password: '123456',
-  },
-];
-
-const customers = [
-  {
-    id: '3958dc9e-712f-4377-85e9-fec4b6a6442a',
-    name: 'Delba de Oliveira',
-    email: 'delba@oliveira.com',
-    image_url: '/customers/delba-de-oliveira.png',
-  },
-  {
-    id: '3958dc9e-742f-4377-85e9-fec4b6a6442a',
-    name: 'Lee Robinson',
-    email: 'lee@robinson.com',
-    image_url: '/customers/lee-robinson.png',
-  },
-  {
-    id: '3958dc9e-737f-4377-85e9-fec4b6a6442a',
-    name: 'Hector Simpson',
-    email: 'hector@simpson.com',
-    image_url: '/customers/hector-simpson.png',
-  },
-  {
-    id: '50ca3e18-62cd-11ee-8c99-0242ac120002',
-    name: 'Steven Tey',
-    email: 'steven@tey.com',
-    image_url: '/customers/steven-tey.png',
-  },
-  {
-    id: '3958dc9e-787f-4377-85e9-fec4b6a6442a',
-    name: 'Steph Dietz',
-    email: 'steph@dietz.com',
-    image_url: '/customers/steph-dietz.png',
-  },
-  {
-    id: '76d65c26-f784-44a2-ac19-586678f7c2f2',
-    name: 'Michael Novotny',
-    email: 'michael@novotny.com',
-    image_url: '/customers/michael-novotny.png',
-  },
-  {
-    id: 'd6e15727-9fe1-4961-8c5b-ea44a9bd81aa',
-    name: 'Evil Rabbit',
-    email: 'evil@rabbit.com',
-    image_url: '/customers/evil-rabbit.png',
-  },
-  {
-    id: '126eed9c-c90c-4ef6-a4a8-fcf7408d3c66',
-    name: 'Emil Kowalski',
-    email: 'emil@kowalski.com',
-    image_url: '/customers/emil-kowalski.png',
-  },
-  {
-    id: 'CC27C14A-0ACF-4F4A-A6C9-D45682C144B9',
-    name: 'Amy Burns',
-    email: 'amy@burns.com',
-    image_url: '/customers/amy-burns.png',
-  },
-  {
-    id: '13D07535-C59E-4157-A011-F8D2EF4E0CBB',
-    name: 'Balazs Orban',
-    email: 'balazs@orban.com',
-    image_url: '/customers/balazs-orban.png',
-  },
-];
-
-const invoices = [
-  {
-    customer_id: customers[0].id,
-    amount: 15795,
-    status: 'pending',
-    date: '2022-12-06',
-  },
-  {
-    customer_id: customers[1].id,
-    amount: 20348,
-    status: 'pending',
-    date: '2022-11-14',
-  },
-  {
-    customer_id: customers[4].id,
-    amount: 3040,
-    status: 'paid',
-    date: '2022-10-29',
-  },
-  {
-    customer_id: customers[3].id,
-    amount: 44800,
-    status: 'paid',
-    date: '2023-09-10',
-  },
-  {
-    customer_id: customers[5].id,
-    amount: 34577,
-    status: 'pending',
-    date: '2023-08-05',
-  },
-  {
-    customer_id: customers[7].id,
-    amount: 54246,
-    status: 'pending',
-    date: '2023-07-16',
-  },
-  {
-    customer_id: customers[6].id,
-    amount: 666,
-    status: 'pending',
-    date: '2023-06-27',
-  },
-  {
-    customer_id: customers[3].id,
-    amount: 32545,
-    status: 'paid',
-    date: '2023-06-09',
-  },
-  {
-    customer_id: customers[4].id,
-    amount: 1250,
-    status: 'paid',
-    date: '2023-06-17',
-  },
-  {
-    customer_id: customers[5].id,
-    amount: 8546,
-    status: 'paid',
-    date: '2023-06-07',
-  },
-  {
-    customer_id: customers[1].id,
-    amount: 500,
-    status: 'paid',
-    date: '2023-08-19',
-  },
-  {
-    customer_id: customers[5].id,
-    amount: 8945,
-    status: 'paid',
-    date: '2023-06-03',
-  },
-  {
-    customer_id: customers[2].id,
-    amount: 8945,
-    status: 'paid',
-    date: '2023-06-18',
-  },
-  {
-    customer_id: customers[0].id,
-    amount: 8945,
-    status: 'paid',
-    date: '2023-10-04',
-  },
-  {
-    customer_id: customers[2].id,
-    amount: 1000,
-    status: 'paid',
-    date: '2022-06-05',
-  },
-];
-
-const revenue = [
-  { month: 'Jan', revenue: 2000 },
-  { month: 'Feb', revenue: 1800 },
-  { month: 'Mar', revenue: 2200 },
-  { month: 'Apr', revenue: 2500 },
-  { month: 'May', revenue: 2300 },
-  { month: 'Jun', revenue: 3200 },
-  { month: 'Jul', revenue: 3500 },
-  { month: 'Aug', revenue: 3700 },
-  { month: 'Sep', revenue: 2500 },
-  { month: 'Oct', revenue: 2800 },
-  { month: 'Nov', revenue: 3000 },
-  { month: 'Dec', revenue: 4800 },
-];
-
-module.exports = {
-  users,
-  customers,
-  invoices,
-  revenue,
-};
diff --git a/src/app/lib/utils.ts b/src/app/lib/utils.ts
index c3184af..b7b6e70 100644
--- a/src/app/lib/utils.ts
+++ b/src/app/lib/utils.ts
@@ -1,11 +1,5 @@
 import Revenue from "@/feature/core/revenue/domain/entity/revenue";
 
-export const formatCurrency = (amount: number) => {
-  return (amount / 100).toLocaleString('en-US', {
-    style: 'currency',
-    currency: 'USD',
-  });
-};
 
 export const generateYAxis = (revenue: Revenue[]) => {
   // Calculate what labels we need to display on the y-axis
diff --git a/src/bootstrap/helpers/global-helpers.ts b/src/bootstrap/helpers/global-helpers.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/feature/common/feature-helpers.ts b/src/feature/common/feature-helpers.ts
new file mode 100644
index 0000000..4269137
--- /dev/null
+++ b/src/feature/common/feature-helpers.ts
@@ -0,0 +1,6 @@
+export const formatCurrency = (amount: number) => {
+  return (amount / 100).toLocaleString('en-US', {
+    style: 'currency',
+    currency: 'USD',
+  });
+};
\ No newline at end of file
diff --git a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
index 18f7db1..ba6eff5 100644
--- a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
+++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -1,5 +1,5 @@
-import { formatCurrency } from "@/app/lib/utils";
 import { sql } from "@/bootstrap/db/db";
+import { formatCurrency } from "@/feature/common/feature-helpers";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 import CustomerInvoiceRepo from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
 import postgres from "postgres";
diff --git a/src/feature/core/customer/data/repo/customer-db-repo.ts b/src/feature/core/customer/data/repo/customer-db-repo.ts
index ad6705f..f1239a4 100644
--- a/src/feature/core/customer/data/repo/customer-db-repo.ts
+++ b/src/feature/core/customer/data/repo/customer-db-repo.ts
@@ -1,8 +1,7 @@
-import { formatCurrency } from "@/app/lib/utils";
 import { sql } from "@/bootstrap/db/db";
+import { formatCurrency } from "@/feature/common/feature-helpers";
 import Customer from "@/feature/core/customer/domain/entity/customer";
 import CustomerRepo from "@/feature/core/customer/domain/i-repo/customer-repo";
-import { connection } from "next/server";
 import postgres from "postgres";
 
 type customerDbResponse = {
diff --git a/src/feature/core/invoice/data/repo/invoice-db-repo.ts b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
index c9bf5e9..66c0998 100644
--- a/src/feature/core/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
@@ -1,5 +1,5 @@
-import { formatCurrency } from "@/app/lib/utils";
 import { sql } from "@/bootstrap/db/db";
+import { formatCurrency } from "@/feature/common/feature-helpers";
 import InvoiceRepo from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
 import postgres from "postgres";
-- 
2.39.5


From c85dd44029f31cdc41ccf2663abe9f8a5a1ed36a Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 18:16:12 +0300
Subject: [PATCH 18/37] Update location of skeleton for dashboard

---
 src/app/dashboard/(overview)/loading.tsx                        | 2 +-
 src/app/dashboard/(overview)/page.tsx                           | 2 +-
 .../skeleton => dashboard/components/skeletons}/skeletons.tsx   | 0
 3 files changed, 2 insertions(+), 2 deletions(-)
 rename src/app/{components/skeleton => dashboard/components/skeletons}/skeletons.tsx (100%)

diff --git a/src/app/dashboard/(overview)/loading.tsx b/src/app/dashboard/(overview)/loading.tsx
index fcf5df1..40c27b6 100644
--- a/src/app/dashboard/(overview)/loading.tsx
+++ b/src/app/dashboard/(overview)/loading.tsx
@@ -1,4 +1,4 @@
-import DashboardSkeleton from "@/app/components/skeleton/skeletons";
+import DashboardSkeleton from "@/app/dashboard/components/skeletons/skeletons";
 
 export default function Loading() {
   return <DashboardSkeleton />;
diff --git a/src/app/dashboard/(overview)/page.tsx b/src/app/dashboard/(overview)/page.tsx
index 463edbb..c6b1b07 100644
--- a/src/app/dashboard/(overview)/page.tsx
+++ b/src/app/dashboard/(overview)/page.tsx
@@ -1,4 +1,4 @@
-import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/components/skeleton/skeletons";
+import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/dashboard/components/skeletons/skeletons";
 import CardWrapper from "@/app/dashboard/components/cards/cards";
 import LatestInvoices from "@/app/dashboard/components/latest-invoices";
 import RevenueChart from "@/app/dashboard/components/revenue-chart";
diff --git a/src/app/components/skeleton/skeletons.tsx b/src/app/dashboard/components/skeletons/skeletons.tsx
similarity index 100%
rename from src/app/components/skeleton/skeletons.tsx
rename to src/app/dashboard/components/skeletons/skeletons.tsx
-- 
2.39.5


From 22e97cf74f001e566c5c4ad69b6da6738bea9cfb Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 18:20:23 +0300
Subject: [PATCH 19/37] Update revenue chart architecture to follow controller
 architecture

---
 src/app/dashboard/(overview)/page.tsx            |  2 +-
 .../revenue-chart/revenue-chart-controller.ts}   | 16 +++++++++++++++-
 .../{ => revenue-chart}/revenue-chart.tsx        |  9 ++-------
 3 files changed, 18 insertions(+), 9 deletions(-)
 rename src/app/{lib/utils.ts => dashboard/components/revenue-chart/revenue-chart-controller.ts} (52%)
 rename src/app/dashboard/components/{ => revenue-chart}/revenue-chart.tsx (84%)

diff --git a/src/app/dashboard/(overview)/page.tsx b/src/app/dashboard/(overview)/page.tsx
index c6b1b07..e6017fc 100644
--- a/src/app/dashboard/(overview)/page.tsx
+++ b/src/app/dashboard/(overview)/page.tsx
@@ -1,7 +1,7 @@
 import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/dashboard/components/skeletons/skeletons";
 import CardWrapper from "@/app/dashboard/components/cards/cards";
 import LatestInvoices from "@/app/dashboard/components/latest-invoices";
-import RevenueChart from "@/app/dashboard/components/revenue-chart";
+import RevenueChart from "@/app/dashboard/components/revenue-chart/revenue-chart";
 import { Suspense } from "react";
 
 export default async function Dashboard() {
diff --git a/src/app/lib/utils.ts b/src/app/dashboard/components/revenue-chart/revenue-chart-controller.ts
similarity index 52%
rename from src/app/lib/utils.ts
rename to src/app/dashboard/components/revenue-chart/revenue-chart-controller.ts
index b7b6e70..becc678 100644
--- a/src/app/lib/utils.ts
+++ b/src/app/dashboard/components/revenue-chart/revenue-chart-controller.ts
@@ -1,7 +1,21 @@
 import Revenue from "@/feature/core/revenue/domain/entity/revenue";
+import fetchRevenuesUsecase from "@/feature/core/revenue/domain/usecase/fetch-revenues-usecase";
 
+export default async function revenueChartController() {
+  const revenue = await fetchRevenuesUsecase();
+  const chartHeight = 350;
 
-export const generateYAxis = (revenue: Revenue[]) => {
+  const { yAxisLabels, topLabel } = generateYAxis(revenue);
+
+  return {
+    revenue,
+    chartHeight,
+    yAxisLabels,
+    topLabel
+  }
+}
+
+function generateYAxis(revenue: Revenue[]) {
   // Calculate what labels we need to display on the y-axis
   // based on highest record and in 1000s
   const yAxisLabels = [];
diff --git a/src/app/dashboard/components/revenue-chart.tsx b/src/app/dashboard/components/revenue-chart/revenue-chart.tsx
similarity index 84%
rename from src/app/dashboard/components/revenue-chart.tsx
rename to src/app/dashboard/components/revenue-chart/revenue-chart.tsx
index f6a6f91..608163c 100644
--- a/src/app/dashboard/components/revenue-chart.tsx
+++ b/src/app/dashboard/components/revenue-chart/revenue-chart.tsx
@@ -1,13 +1,8 @@
-import { generateYAxis } from '@/app/lib/utils';
-import fetchRevenuesUsecase from '@/feature/core/revenue/domain/usecase/fetch-revenues-usecase';
+import revenueChartController from '@/app/dashboard/components/revenue-chart/revenue-chart-controller';
 import { CalendarIcon } from '@heroicons/react/24/outline';
 
 export default async function RevenueChart() {
-  const revenue = await fetchRevenuesUsecase();
-
-  const chartHeight = 350;
-
-  const { yAxisLabels, topLabel } = generateYAxis(revenue);
+  const { chartHeight, revenue, topLabel, yAxisLabels } = await revenueChartController()
 
   if (!revenue || revenue.length === 0) {
     return <p className="mt-4 text-gray-400">No data available.</p>;
-- 
2.39.5


From 38f6a47f6500bed31d05ddc7a7125e937c64b34f Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 18:23:00 +0300
Subject: [PATCH 20/37] Update latest invoices architecture to follow
 controller architecture

---
 src/app/dashboard/(overview)/page.tsx         |  2 +-
 .../latest-invoices-controller.ts             |  5 ++++
 .../{ => latest-invoices}/latest-invoices.tsx | 26 ++++++++++---------
 3 files changed, 20 insertions(+), 13 deletions(-)
 create mode 100644 src/app/dashboard/components/latest-invoices/latest-invoices-controller.ts
 rename src/app/dashboard/components/{ => latest-invoices}/latest-invoices.tsx (88%)

diff --git a/src/app/dashboard/(overview)/page.tsx b/src/app/dashboard/(overview)/page.tsx
index e6017fc..519d478 100644
--- a/src/app/dashboard/(overview)/page.tsx
+++ b/src/app/dashboard/(overview)/page.tsx
@@ -1,6 +1,6 @@
 import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/dashboard/components/skeletons/skeletons";
 import CardWrapper from "@/app/dashboard/components/cards/cards";
-import LatestInvoices from "@/app/dashboard/components/latest-invoices";
+import LatestInvoices from "@/app/dashboard/components/latest-invoices/latest-invoices";
 import RevenueChart from "@/app/dashboard/components/revenue-chart/revenue-chart";
 import { Suspense } from "react";
 
diff --git a/src/app/dashboard/components/latest-invoices/latest-invoices-controller.ts b/src/app/dashboard/components/latest-invoices/latest-invoices-controller.ts
new file mode 100644
index 0000000..45bde22
--- /dev/null
+++ b/src/app/dashboard/components/latest-invoices/latest-invoices-controller.ts
@@ -0,0 +1,5 @@
+import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
+
+export default function latestInvoicesController() {
+    return fetchCustomerInvoicesUsecase()
+}
\ No newline at end of file
diff --git a/src/app/dashboard/components/latest-invoices.tsx b/src/app/dashboard/components/latest-invoices/latest-invoices.tsx
similarity index 88%
rename from src/app/dashboard/components/latest-invoices.tsx
rename to src/app/dashboard/components/latest-invoices/latest-invoices.tsx
index 6f62655..0db6556 100644
--- a/src/app/dashboard/components/latest-invoices.tsx
+++ b/src/app/dashboard/components/latest-invoices/latest-invoices.tsx
@@ -1,19 +1,12 @@
-import fetchCustomerInvoicesUsecase from '@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase';
+import latestInvoicesController from '@/app/dashboard/components/latest-invoices/latest-invoices-controller';
 import { ArrowPathIcon } from '@heroicons/react/24/outline';
 import clsx from 'clsx';
 import Image from 'next/image';
+
 export default async function LatestInvoices() {
-  const latestInvoices = await fetchCustomerInvoicesUsecase();
+  const latestInvoices = await latestInvoicesController();
 
-  return (
-    <div className="flex w-full flex-col md:col-span-4">
-      <h2 className="mb-4 text-xl md:text-2xl">
-        Latest Invoices
-      </h2>
-      <div className="flex grow flex-col justify-between rounded-xl bg-gray-50 p-4">
-
-        <div className="bg-white px-6">
-          {latestInvoices.map((invoice, i) => {
+  const invoices = latestInvoices.map((invoice, i) => {
             return (
               <div
                 key={invoice.id}
@@ -48,7 +41,16 @@ export default async function LatestInvoices() {
                 </p>
               </div>
             );
-          })}
+          })
+  return (
+    <div className="flex w-full flex-col md:col-span-4">
+      <h2 className="mb-4 text-xl md:text-2xl">
+        Latest Invoices
+      </h2>
+      <div className="flex grow flex-col justify-between rounded-xl bg-gray-50 p-4">
+
+        <div className="bg-white px-6">
+          {invoices}
         </div>
         <div className="flex items-center pb-2 pt-6">
           <ArrowPathIcon className="h-5 w-5 text-gray-500" />
-- 
2.39.5


From f522877b1948b02404d10965c704c7110944a5ce Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 18:31:59 +0300
Subject: [PATCH 21/37] Update dashboard main page to follow architecture of
 app layer

---
 src/app/dashboard/(overview)/loading.tsx      |  5 ----
 .../client/nav-links/nav-link-controller.ts   | 29 +++++++++++++++++++
 .../{ => client/nav-links}/nav-links.tsx      | 21 ++------------
 .../{ => server}/card/card-controller.ts      |  0
 .../components/{ => server}/card/card.tsx     |  2 +-
 .../{ => server}/cards/cards-controller.ts    |  0
 .../components/{ => server}/cards/cards.tsx   |  4 +--
 .../latest-invoices-controller.ts             |  0
 .../latest-invoices/latest-invoices.tsx       |  2 +-
 .../revenue-chart/revenue-chart-controller.ts |  0
 .../revenue-chart/revenue-chart.tsx           |  2 +-
 .../components/{ => server}/sidenav.tsx       |  2 +-
 .../{ => server}/skeletons/skeletons.tsx      |  0
 src/app/dashboard/layout.tsx                  |  2 +-
 src/app/dashboard/loading.tsx                 |  5 ++++
 src/app/dashboard/{(overview) => }/page.tsx   |  8 ++---
 16 files changed, 48 insertions(+), 34 deletions(-)
 delete mode 100644 src/app/dashboard/(overview)/loading.tsx
 create mode 100644 src/app/dashboard/components/client/nav-links/nav-link-controller.ts
 rename src/app/dashboard/components/{ => client/nav-links}/nav-links.tsx (51%)
 rename src/app/dashboard/components/{ => server}/card/card-controller.ts (100%)
 rename src/app/dashboard/components/{ => server}/card/card.tsx (87%)
 rename src/app/dashboard/components/{ => server}/cards/cards-controller.ts (100%)
 rename src/app/dashboard/components/{ => server}/cards/cards.tsx (76%)
 rename src/app/dashboard/components/{ => server}/latest-invoices/latest-invoices-controller.ts (100%)
 rename src/app/dashboard/components/{ => server}/latest-invoices/latest-invoices.tsx (97%)
 rename src/app/dashboard/components/{ => server}/revenue-chart/revenue-chart-controller.ts (100%)
 rename src/app/dashboard/components/{ => server}/revenue-chart/revenue-chart.tsx (93%)
 rename src/app/dashboard/components/{ => server}/sidenav.tsx (88%)
 rename src/app/dashboard/components/{ => server}/skeletons/skeletons.tsx (100%)
 create mode 100644 src/app/dashboard/loading.tsx
 rename src/app/dashboard/{(overview) => }/page.tsx (68%)

diff --git a/src/app/dashboard/(overview)/loading.tsx b/src/app/dashboard/(overview)/loading.tsx
deleted file mode 100644
index 40c27b6..0000000
--- a/src/app/dashboard/(overview)/loading.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import DashboardSkeleton from "@/app/dashboard/components/skeletons/skeletons";
-
-export default function Loading() {
-  return <DashboardSkeleton />;
-}
\ No newline at end of file
diff --git a/src/app/dashboard/components/client/nav-links/nav-link-controller.ts b/src/app/dashboard/components/client/nav-links/nav-link-controller.ts
new file mode 100644
index 0000000..b6a3aa9
--- /dev/null
+++ b/src/app/dashboard/components/client/nav-links/nav-link-controller.ts
@@ -0,0 +1,29 @@
+
+import { DocumentIcon } from '@/app/components/icons/document';
+import HomeIcon from '@/app/components/icons/home';
+import { UserIcon } from '@/app/components/icons/user';
+import { usePathname } from 'next/navigation';
+
+type LinkItem = {
+    name: string; 
+    href: string;
+    icon: (props: {className?: string}) => JSX.Element
+}
+export default function navLinkPersonalVM() {
+    const pathname = usePathname()
+    // Map of links to display in the side navigation.
+    // Depending on the size of the application, this would be stored in a database.
+    const links: LinkItem[] = [
+        { name: 'Home', href: '/dashboard', icon: HomeIcon },
+        {
+            name: 'Invoices',
+            href: '/dashboard/invoices',
+            icon: DocumentIcon,
+        },
+        { name: 'Customers', href: '/dashboard/customers', icon: UserIcon },
+    ];
+    return {
+        links,
+        isLinkActive: (link: LinkItem) => pathname === link.href
+    }
+}
\ No newline at end of file
diff --git a/src/app/dashboard/components/nav-links.tsx b/src/app/dashboard/components/client/nav-links/nav-links.tsx
similarity index 51%
rename from src/app/dashboard/components/nav-links.tsx
rename to src/app/dashboard/components/client/nav-links/nav-links.tsx
index ecfca9e..1aa6072 100644
--- a/src/app/dashboard/components/nav-links.tsx
+++ b/src/app/dashboard/components/client/nav-links/nav-links.tsx
@@ -1,25 +1,10 @@
 'use client'
-import { DocumentIcon } from '@/app/components/icons/document';
-import HomeIcon from '@/app/components/icons/home';
-import { UserIcon } from '@/app/components/icons/user';
+import navLinkPersonalVM from '@/app/dashboard/components/client/nav-links/nav-link-controller';
 import clsx from 'clsx';
 import Link from 'next/link'
-import { usePathname } from 'next/navigation';
-
-// Map of links to display in the side navigation.
-// Depending on the size of the application, this would be stored in a database.
-const links = [
-  { name: 'Home', href: '/dashboard', icon: HomeIcon },
-  {
-    name: 'Invoices',
-    href: '/dashboard/invoices',
-    icon: DocumentIcon,
-  },
-  { name: 'Customers', href: '/dashboard/customers', icon: UserIcon },
-];
 
 export default function NavLinks() {
-  const pathname = usePathname()
+  const { links, isLinkActive  } = navLinkPersonalVM()
   return (
     <>
       {links.map((link) => {
@@ -31,7 +16,7 @@ export default function NavLinks() {
             className={clsx(
               'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3',
               {
-                'bg-sky-100 text-blue-600': pathname === link.href,
+                'bg-sky-100 text-blue-600': isLinkActive(link),
               },
             )}
           >
diff --git a/src/app/dashboard/components/card/card-controller.ts b/src/app/dashboard/components/server/card/card-controller.ts
similarity index 100%
rename from src/app/dashboard/components/card/card-controller.ts
rename to src/app/dashboard/components/server/card/card-controller.ts
diff --git a/src/app/dashboard/components/card/card.tsx b/src/app/dashboard/components/server/card/card.tsx
similarity index 87%
rename from src/app/dashboard/components/card/card.tsx
rename to src/app/dashboard/components/server/card/card.tsx
index 5494f4f..1b22154 100644
--- a/src/app/dashboard/components/card/card.tsx
+++ b/src/app/dashboard/components/server/card/card.tsx
@@ -1,4 +1,4 @@
-import cardController from "@/app/dashboard/components/card/card-controller";
+import cardController from "@/app/dashboard/components/server/card/card-controller";
 
 
 
diff --git a/src/app/dashboard/components/cards/cards-controller.ts b/src/app/dashboard/components/server/cards/cards-controller.ts
similarity index 100%
rename from src/app/dashboard/components/cards/cards-controller.ts
rename to src/app/dashboard/components/server/cards/cards-controller.ts
diff --git a/src/app/dashboard/components/cards/cards.tsx b/src/app/dashboard/components/server/cards/cards.tsx
similarity index 76%
rename from src/app/dashboard/components/cards/cards.tsx
rename to src/app/dashboard/components/server/cards/cards.tsx
index c39a4fe..e8f74a9 100644
--- a/src/app/dashboard/components/cards/cards.tsx
+++ b/src/app/dashboard/components/server/cards/cards.tsx
@@ -1,5 +1,5 @@
-import { Card } from '@/app/dashboard/components/card/card';
-import cardsController from '@/app/dashboard/components/cards/cards-controller';
+import { Card } from '@/app/dashboard/components/server/card/card';
+import cardsController from '@/app/dashboard/components/server/cards/cards-controller';
 
 
 export default async function CardWrapper() {
diff --git a/src/app/dashboard/components/latest-invoices/latest-invoices-controller.ts b/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
similarity index 100%
rename from src/app/dashboard/components/latest-invoices/latest-invoices-controller.ts
rename to src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
diff --git a/src/app/dashboard/components/latest-invoices/latest-invoices.tsx b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
similarity index 97%
rename from src/app/dashboard/components/latest-invoices/latest-invoices.tsx
rename to src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
index 0db6556..dc3c7be 100644
--- a/src/app/dashboard/components/latest-invoices/latest-invoices.tsx
+++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
@@ -1,4 +1,4 @@
-import latestInvoicesController from '@/app/dashboard/components/latest-invoices/latest-invoices-controller';
+import latestInvoicesController from '@/app/dashboard/components/server/latest-invoices/latest-invoices-controller';
 import { ArrowPathIcon } from '@heroicons/react/24/outline';
 import clsx from 'clsx';
 import Image from 'next/image';
diff --git a/src/app/dashboard/components/revenue-chart/revenue-chart-controller.ts b/src/app/dashboard/components/server/revenue-chart/revenue-chart-controller.ts
similarity index 100%
rename from src/app/dashboard/components/revenue-chart/revenue-chart-controller.ts
rename to src/app/dashboard/components/server/revenue-chart/revenue-chart-controller.ts
diff --git a/src/app/dashboard/components/revenue-chart/revenue-chart.tsx b/src/app/dashboard/components/server/revenue-chart/revenue-chart.tsx
similarity index 93%
rename from src/app/dashboard/components/revenue-chart/revenue-chart.tsx
rename to src/app/dashboard/components/server/revenue-chart/revenue-chart.tsx
index 608163c..b29a7ae 100644
--- a/src/app/dashboard/components/revenue-chart/revenue-chart.tsx
+++ b/src/app/dashboard/components/server/revenue-chart/revenue-chart.tsx
@@ -1,4 +1,4 @@
-import revenueChartController from '@/app/dashboard/components/revenue-chart/revenue-chart-controller';
+import revenueChartController from '@/app/dashboard/components/server/revenue-chart/revenue-chart-controller';
 import { CalendarIcon } from '@heroicons/react/24/outline';
 
 export default async function RevenueChart() {
diff --git a/src/app/dashboard/components/sidenav.tsx b/src/app/dashboard/components/server/sidenav.tsx
similarity index 88%
rename from src/app/dashboard/components/sidenav.tsx
rename to src/app/dashboard/components/server/sidenav.tsx
index 7cf3d44..0af5a13 100644
--- a/src/app/dashboard/components/sidenav.tsx
+++ b/src/app/dashboard/components/server/sidenav.tsx
@@ -1,4 +1,4 @@
-import NavLinks from '@/app/dashboard/components/nav-links';
+import NavLinks from '@/app/dashboard/components/client/nav-links/nav-links';
 import Link from 'next/link';
 
 export default function SideNav() {
diff --git a/src/app/dashboard/components/skeletons/skeletons.tsx b/src/app/dashboard/components/server/skeletons/skeletons.tsx
similarity index 100%
rename from src/app/dashboard/components/skeletons/skeletons.tsx
rename to src/app/dashboard/components/server/skeletons/skeletons.tsx
diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx
index d28eae9..75b7ecb 100644
--- a/src/app/dashboard/layout.tsx
+++ b/src/app/dashboard/layout.tsx
@@ -1,4 +1,4 @@
-import SideNav from "@/app/dashboard/components/sidenav";
+import SideNav from "@/app/dashboard/components/server/sidenav";
 
  
 export default function Layout({ children }: { children: React.ReactNode }) {
diff --git a/src/app/dashboard/loading.tsx b/src/app/dashboard/loading.tsx
new file mode 100644
index 0000000..c50ad40
--- /dev/null
+++ b/src/app/dashboard/loading.tsx
@@ -0,0 +1,5 @@
+import DashboardSkeleton from "@/app/dashboard/components/server/skeletons/skeletons";
+
+export default function Loading() {
+  return <DashboardSkeleton />;
+}
\ No newline at end of file
diff --git a/src/app/dashboard/(overview)/page.tsx b/src/app/dashboard/page.tsx
similarity index 68%
rename from src/app/dashboard/(overview)/page.tsx
rename to src/app/dashboard/page.tsx
index 519d478..24fb89a 100644
--- a/src/app/dashboard/(overview)/page.tsx
+++ b/src/app/dashboard/page.tsx
@@ -1,7 +1,7 @@
-import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/dashboard/components/skeletons/skeletons";
-import CardWrapper from "@/app/dashboard/components/cards/cards";
-import LatestInvoices from "@/app/dashboard/components/latest-invoices/latest-invoices";
-import RevenueChart from "@/app/dashboard/components/revenue-chart/revenue-chart";
+import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/dashboard/components/server/skeletons/skeletons";
+import CardWrapper from "@/app/dashboard/components/server/cards/cards";
+import LatestInvoices from "@/app/dashboard/components/server/latest-invoices/latest-invoices";
+import RevenueChart from "@/app/dashboard/components/server/revenue-chart/revenue-chart";
 import { Suspense } from "react";
 
 export default async function Dashboard() {
-- 
2.39.5


From f375de27ec7437bf976c6bec4770e68ec7472781 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 18:47:24 +0300
Subject: [PATCH 22/37] Add button and update shadcn configs

---
 components.json                               | 10 ++--
 package.json                                  |  1 +
 src/app/components/button/button.tsx          | 59 ++++++++++++++++++-
 src/{app => bootstrap/helpers}/lib/actions.ts |  0
 .../helpers/lib/ui-utils.ts}                  |  0
 src/bootstrap/helpers/view/base-view.tsx      |  8 ++-
 yarn.lock                                     | 12 ++++
 7 files changed, 82 insertions(+), 8 deletions(-)
 rename src/{app => bootstrap/helpers}/lib/actions.ts (100%)
 rename src/{lib/utils.ts => bootstrap/helpers/lib/ui-utils.ts} (100%)

diff --git a/components.json b/components.json
index 7a63543..a0c8a84 100644
--- a/components.json
+++ b/components.json
@@ -11,10 +11,10 @@
     "prefix": ""
   },
   "aliases": {
-    "components": "@/components",
-    "utils": "@/lib/utils",
-    "ui": "@/components/ui",
-    "lib": "@/lib",
-    "hooks": "@/hooks"
+    "components": "@/app/components",
+    "utils": "@/bootstrap/helpers/lib/ui-utils",
+    "ui": "@/app/components",
+    "lib": "@/bootstrap/helpers/lib",
+    "hooks": "@/bootstrap/helpers/vm/hooks"
   }
 }
\ No newline at end of file
diff --git a/package.json b/package.json
index 2a11c38..62211ce 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
   "dependencies": {
     "@heroicons/react": "^2.1.5",
     "@radix-ui/react-icons": "^1.3.1",
+    "@radix-ui/react-slot": "^1.1.0",
     "class-variance-authority": "^0.7.0",
     "clsx": "^2.1.1",
     "lucide-react": "^0.454.0",
diff --git a/src/app/components/button/button.tsx b/src/app/components/button/button.tsx
index e1b8a4d..6d0c377 100644
--- a/src/app/components/button/button.tsx
+++ b/src/app/components/button/button.tsx
@@ -2,11 +2,68 @@
 import BaseView, { BuildProps } from "@/bootstrap/helpers/view/base-view";
 import ButtonVm from "@/app/components/button/button-vm";
 import { ReactNode } from "react";
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+import { cn } from "@/bootstrap/helpers/lib/ui-utils";
 
 export default class Button extends BaseView<ButtonVm> {
     protected Build(props: BuildProps<ButtonVm>): ReactNode {
         const {vm} = props
         
-        return <button onClick={vm.onClick} >{vm.props.title}</button>
+        return <ButtonUi onClick={vm.onClick} >{vm.props.title}</ButtonUi> 
     }
 }
+
+
+const buttonVariants = cva(
+  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+  {
+    variants: {
+      variant: {
+        default:
+          "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+        destructive:
+          "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+        outline:
+          "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+        secondary:
+          "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+        ghost: "hover:bg-accent hover:text-accent-foreground",
+        link: "text-primary underline-offset-4 hover:underline",
+      },
+      size: {
+        default: "h-9 px-4 py-2",
+        sm: "h-8 rounded-md px-3 text-xs",
+        lg: "h-10 rounded-md px-8",
+        icon: "h-9 w-9",
+      },
+    },
+    defaultVariants: {
+      variant: "default",
+      size: "default",
+    },
+  }
+)
+
+export interface ButtonProps
+  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
+    VariantProps<typeof buttonVariants> {
+  asChild?: boolean
+}
+
+const ButtonUi = React.forwardRef<HTMLButtonElement, ButtonProps>(
+  ({ className, variant, size, asChild = false, ...props }, ref) => {
+    const Comp = asChild ? Slot : "button"
+    return (
+      <Comp
+        className={cn(buttonVariants({ variant, size, className }))}
+        ref={ref}
+        {...props}
+      />
+    )
+  }
+)
+ButtonUi.displayName = "Button"
+
+export { Button, buttonVariants }
\ No newline at end of file
diff --git a/src/app/lib/actions.ts b/src/bootstrap/helpers/lib/actions.ts
similarity index 100%
rename from src/app/lib/actions.ts
rename to src/bootstrap/helpers/lib/actions.ts
diff --git a/src/lib/utils.ts b/src/bootstrap/helpers/lib/ui-utils.ts
similarity index 100%
rename from src/lib/utils.ts
rename to src/bootstrap/helpers/lib/ui-utils.ts
diff --git a/src/bootstrap/helpers/view/base-view.tsx b/src/bootstrap/helpers/view/base-view.tsx
index a1eecc7..990cd19 100644
--- a/src/bootstrap/helpers/view/base-view.tsx
+++ b/src/bootstrap/helpers/view/base-view.tsx
@@ -69,13 +69,17 @@ export default abstract class BaseView<
   IVM extends IVMParent,
   PROPS extends IPropParent = undefined,
 > extends Component<BaseProps<IVM, PROPS>> {
-  /* -------------------------------- Abstracts ------------------------------- */
   protected abstract Build(props: BuildProps<IVM, PROPS>): ReactNode;
 
-  /* -------------------------------- Renderer -------------------------------- */
+  protected get componentName() {
+    return this.constructor.name
+  }
+
   render(): ReactNode {
     const { vm, restProps, memoizedByVM, children, ...rest } = this.props;
     
+    VvmConnector.displayName = this.componentName
+  
     return (
       <VvmConnector
         View={this.Build}
diff --git a/yarn.lock b/yarn.lock
index 1a06bc5..6d7dde4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -613,11 +613,23 @@
   resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
   integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
 
+"@radix-ui/react-compose-refs@1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74"
+  integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==
+
 "@radix-ui/react-icons@^1.3.1":
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.1.tgz#462c85fd726a77854cd5956e29eb19a575aa7ce5"
   integrity sha512-QvYompk0X+8Yjlo/Fv4McrzxohDdM5GgLHyQcPpcsPvlOSXCGFjdbuyGL5dzRbg0GpknAjQJJZzdiRK7iWVuFQ==
 
+"@radix-ui/react-slot@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84"
+  integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==
+  dependencies:
+    "@radix-ui/react-compose-refs" "1.1.0"
+
 "@rollup/rollup-android-arm-eabi@4.24.3":
   version "4.24.3"
   resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz#49a2a9808074f2683667992aa94b288e0b54fc82"
-- 
2.39.5


From 96e7811fff23adbc5cc64a720ef11e0208b908d0 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 18:59:23 +0300
Subject: [PATCH 23/37] Add base vm for create invoice button

---
 src/app/components/button/button.tsx          |  2 +-
 .../create-random-invoice.tsx                 | 13 +++++++++++
 .../latest-invoices/latest-invoices.tsx       |  4 +++-
 src/app/dashboard/layout.tsx                  | 17 +++++++++-----
 .../dashboard/module/dashboard-app-module.ts  | 22 ++-----------------
 .../vm/create-random-invoice-button-vm.ts     | 15 +++++++++++++
 6 files changed, 46 insertions(+), 27 deletions(-)
 create mode 100644 src/app/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
 create mode 100644 src/app/dashboard/vm/create-random-invoice-button-vm.ts

diff --git a/src/app/components/button/button.tsx b/src/app/components/button/button.tsx
index 6d0c377..5245285 100644
--- a/src/app/components/button/button.tsx
+++ b/src/app/components/button/button.tsx
@@ -66,4 +66,4 @@ const ButtonUi = React.forwardRef<HTMLButtonElement, ButtonProps>(
 )
 ButtonUi.displayName = "Button"
 
-export { Button, buttonVariants }
\ No newline at end of file
+export { buttonVariants }
\ No newline at end of file
diff --git a/src/app/dashboard/components/client/create-random-invoice/create-random-invoice.tsx b/src/app/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
new file mode 100644
index 0000000..8a80645
--- /dev/null
+++ b/src/app/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
@@ -0,0 +1,13 @@
+"use client"
+
+import Button from "@/app/components/button/button"
+import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm"
+import { useDI } from "@/bootstrap/di/di-context"
+import { useRef } from "react"
+
+export default function CreateRandomInvoiceContainer() {
+    const di = useDI()
+    const vm = useRef(di.resolve(CreateRandomInvoiceButtonVM))
+
+    return <Button vm={vm.current}/>
+}
\ No newline at end of file
diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
index dc3c7be..09b140b 100644
--- a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
+++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
@@ -1,3 +1,4 @@
+import CreateRandomInvoiceContainer from '@/app/dashboard/components/client/create-random-invoice/create-random-invoice';
 import latestInvoicesController from '@/app/dashboard/components/server/latest-invoices/latest-invoices-controller';
 import { ArrowPathIcon } from '@heroicons/react/24/outline';
 import clsx from 'clsx';
@@ -52,10 +53,11 @@ export default async function LatestInvoices() {
         <div className="bg-white px-6">
           {invoices}
         </div>
-        <div className="flex items-center pb-2 pt-6">
+        <div className="flex items-end mt-auto pb-2 pt-6">
           <ArrowPathIcon className="h-5 w-5 text-gray-500" />
           <h3 className="ml-2 text-sm text-gray-500 ">Updated just now</h3>
         </div>
+        <CreateRandomInvoiceContainer />
       </div>
     </div>
   );
diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx
index 75b7ecb..55125e7 100644
--- a/src/app/dashboard/layout.tsx
+++ b/src/app/dashboard/layout.tsx
@@ -1,13 +1,20 @@
+"use client"
 import SideNav from "@/app/dashboard/components/server/sidenav";
+import dashboardAppModule from "@/app/dashboard/module/dashboard-app-module";
+import { DiContext } from "@/bootstrap/di/di-context";
+import { useRef } from "react";
 
  
 export default function Layout({ children }: { children: React.ReactNode }) {
+  const di = useRef(dashboardAppModule())
   return (
-    <div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
-      <div className="w-full flex-none md:w-64">
-        <SideNav />
+    <DiContext.Provider value={di.current}>
+      <div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
+        <div className="w-full flex-none md:w-64">
+          <SideNav />
+        </div>
+        <div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
       </div>
-      <div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
-    </div>
+    </DiContext.Provider>
   );
 }
\ No newline at end of file
diff --git a/src/app/dashboard/module/dashboard-app-module.ts b/src/app/dashboard/module/dashboard-app-module.ts
index fd9f144..a3611c1 100644
--- a/src/app/dashboard/module/dashboard-app-module.ts
+++ b/src/app/dashboard/module/dashboard-app-module.ts
@@ -1,27 +1,9 @@
+import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm";
 import di from "@/bootstrap/di/init-di"
-import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
-import fetchCustomersUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-usecase";
-import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase";
-import fetchRevenuesUsecase from "@/feature/core/revenue/domain/usecase/fetch-revenues-usecase";
 
 export default function dashboardAppModule() {
     const dashboardDi = di.createChildContainer()
     
-    dashboardDi.register(fetchCustomersUsecase.name, {
-        useValue: fetchCustomersUsecase
-    })
-
-    dashboardDi.register(fetchAllInvoicesAmountUsecase.name, {
-        useValue: fetchAllInvoicesAmountUsecase
-    })
-    dashboardDi.register(fetchAllInvoicesAmountUsecase.name, {
-        useValue: fetchAllInvoicesAmountUsecase
-    })
-    dashboardDi.register(fetchCustomerInvoicesUsecase.name, {
-        useValue: fetchCustomerInvoicesUsecase
-    })
-    dashboardDi.register(fetchRevenuesUsecase.name, {
-        useValue: fetchRevenuesUsecase
-    })
+    dashboardDi.register(CreateRandomInvoiceButtonVM, CreateRandomInvoiceButtonVM)
     return dashboardDi
 }
diff --git a/src/app/dashboard/vm/create-random-invoice-button-vm.ts b/src/app/dashboard/vm/create-random-invoice-button-vm.ts
new file mode 100644
index 0000000..3bbf802
--- /dev/null
+++ b/src/app/dashboard/vm/create-random-invoice-button-vm.ts
@@ -0,0 +1,15 @@
+import ButtonVm from "@/app/components/button/button-vm";
+import BaseVM from "@/bootstrap/helpers/vm/base-vm";
+
+export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
+    useVM(): ButtonVm {
+        return {
+            props: {
+                title: "Button Title"
+            },
+            onClick: () => {
+                console.log('clicked');
+            }
+        }
+    }
+} 
\ No newline at end of file
-- 
2.39.5


From 021511e60e8f0aefce9e95a22448bc8c5037b999 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 1 Nov 2024 20:00:16 +0300
Subject: [PATCH 24/37] Add create random invoice module

---
 components.json                               |  2 +-
 .../latest-invoices/latest-invoices.tsx       |  4 +--
 .../dashboard/module/dashboard-app-module.ts  |  4 +++
 .../vm/create-random-invoice-button-vm.ts     | 29 ++++++++++++++++---
 src/bootstrap/helpers/hooks/use-throttle.ts   | 18 ++++++++++++
 .../data/repo/customer-invoice-db-repo.ts     |  2 +-
 .../core/invoice/data/repo/invoice-db-repo.ts | 23 +++++++++++++++
 .../invoice/domain/i-repo/invoice-repo.ts     |  2 ++
 .../invoice/domain/param/invoice-param.ts     | 10 +++++++
 .../domain/usecase/create-invoice-usecase.ts  | 22 ++++++++++++++
 10 files changed, 108 insertions(+), 8 deletions(-)
 create mode 100644 src/bootstrap/helpers/hooks/use-throttle.ts
 create mode 100644 src/feature/core/invoice/domain/param/invoice-param.ts
 create mode 100644 src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts

diff --git a/components.json b/components.json
index a0c8a84..88a82c6 100644
--- a/components.json
+++ b/components.json
@@ -15,6 +15,6 @@
     "utils": "@/bootstrap/helpers/lib/ui-utils",
     "ui": "@/app/components",
     "lib": "@/bootstrap/helpers/lib",
-    "hooks": "@/bootstrap/helpers/vm/hooks"
+    "hooks": "@/bootstrap/helpers/hooks"
   }
 }
\ No newline at end of file
diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
index 09b140b..09396cd 100644
--- a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
+++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
@@ -48,9 +48,9 @@ export default async function LatestInvoices() {
       <h2 className="mb-4 text-xl md:text-2xl">
         Latest Invoices
       </h2>
-      <div className="flex grow flex-col justify-between rounded-xl bg-gray-50 p-4">
+      <div className="flex grow flex-col max-h-[66.5vh] justify-between rounded-xl bg-gray-50 p-4">
 
-        <div className="bg-white px-6">
+        <div className="bg-white px-6 h-full overflow-y-auto">
           {invoices}
         </div>
         <div className="flex items-end mt-auto pb-2 pt-6">
diff --git a/src/app/dashboard/module/dashboard-app-module.ts b/src/app/dashboard/module/dashboard-app-module.ts
index a3611c1..60721eb 100644
--- a/src/app/dashboard/module/dashboard-app-module.ts
+++ b/src/app/dashboard/module/dashboard-app-module.ts
@@ -1,9 +1,13 @@
 import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm";
 import di from "@/bootstrap/di/init-di"
+import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
 
 export default function dashboardAppModule() {
     const dashboardDi = di.createChildContainer()
     
+    dashboardDi.register(createInvoiceUsecase.name, {
+        useValue: createInvoiceUsecase
+    })
     dashboardDi.register(CreateRandomInvoiceButtonVM, CreateRandomInvoiceButtonVM)
     return dashboardDi
 }
diff --git a/src/app/dashboard/vm/create-random-invoice-button-vm.ts b/src/app/dashboard/vm/create-random-invoice-button-vm.ts
index 3bbf802..11a258e 100644
--- a/src/app/dashboard/vm/create-random-invoice-button-vm.ts
+++ b/src/app/dashboard/vm/create-random-invoice-button-vm.ts
@@ -1,15 +1,36 @@
 import ButtonVm from "@/app/components/button/button-vm";
+import useThrottle from "@/bootstrap/helpers/hooks/use-throttle";
 import BaseVM from "@/bootstrap/helpers/vm/base-vm";
+import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
+import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
+import { faker } from "@faker-js/faker";
 
 export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
+    private createInvoice: typeof createInvoiceUsecase
+
+    constructor() {
+        super()
+        this.createInvoice = this.di.resolve(createInvoiceUsecase.name)
+    }
+
     useVM(): ButtonVm {
+        const throttledOnClick = useThrottle(this.onClickHandler.bind(this), 5000)
         return {
             props: {
-                title: "Button Title"
+                title: "Create Random Invoice"
             },
-            onClick: () => {
-                console.log('clicked');
-            }
+            onClick: throttledOnClick 
         }
     }
+
+    onClickHandler() {
+        const fakedParams: InvoiceParam = {
+            amount: faker.number.int({
+                min: 1,
+                max: 10
+            }),
+            status: "paid"
+        }
+        this.createInvoice(fakedParams)
+    }
 } 
\ No newline at end of file
diff --git a/src/bootstrap/helpers/hooks/use-throttle.ts b/src/bootstrap/helpers/hooks/use-throttle.ts
new file mode 100644
index 0000000..ff2344f
--- /dev/null
+++ b/src/bootstrap/helpers/hooks/use-throttle.ts
@@ -0,0 +1,18 @@
+"use client"
+
+import { useEffect, useRef } from "react"
+
+/**
+ * 
+ * @param callback 
+ * @param time In miliseconds
+ */
+export default function useThrottle<T extends Function>(callback: T, time: number = 2000) {
+    const lastRun = useRef(Date.now())
+
+    return function() {
+        if (Date.now() - lastRun.current <= time) return;
+        lastRun.current = Date.now()
+        return callback()
+    }
+}
\ No newline at end of file
diff --git a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
index ba6eff5..9c19809 100644
--- a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
+++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -20,7 +20,7 @@ export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
             FROM invoices
             JOIN customers ON invoices.customer_id = customers.id
             ORDER BY invoices.date DESC
-            LIMIT 5` as postgres.RowList<customerInvoiceDbResponse[]>;
+            LIMIT 20 ` as postgres.RowList<customerInvoiceDbResponse[]>;
 
             return this.customerInvoicesDto(data)
         } catch (error) {
diff --git a/src/feature/core/invoice/data/repo/invoice-db-repo.ts b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
index 66c0998..fd2219c 100644
--- a/src/feature/core/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
@@ -1,6 +1,7 @@
 import { sql } from "@/bootstrap/db/db";
 import { formatCurrency } from "@/feature/common/feature-helpers";
 import InvoiceRepo from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
 import postgres from "postgres";
 
@@ -12,6 +13,28 @@ export default class InvoiceDbRepo implements InvoiceRepo {
         return data.count ?? 0
     }
 
+    async createInvoice(params: InvoiceParam): Promise<string> {
+        const firstCustomerIdDb = await sql`SELECT 
+            id FROM customers 
+            ORDER BY id ASC
+            LIMIT 1
+        `
+        const customerId = firstCustomerIdDb.at(0)?.id
+        if (!customerId) throw new Error("There is no customer")
+        
+        const { amount, status } = params;
+        const amountInCents = amount * 100;
+        const date = new Date().toISOString().split('T')[0];
+
+        // Insert data into the database
+        const result = await sql`
+            INSERT INTO invoices (customer_id, amount, status, date)
+            VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
+            RETURNING id
+        `;
+        return result.at(0)?.id ?? ""
+    }
+
     async fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
         const invoiceStatusPromise = await sql`SELECT
             SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
diff --git a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
index 90bac8f..983ac3f 100644
--- a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
+++ b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
@@ -1,8 +1,10 @@
+import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param"
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status"
 
 export default interface InvoiceRepo {
     fetchAllInvoicesAmount(): Promise<number>
     fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary>
+    createInvoice(params: InvoiceParam): Promise<string>
 }
 
 export const invoiceRepoKey = "invoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/core/invoice/domain/param/invoice-param.ts b/src/feature/core/invoice/domain/param/invoice-param.ts
new file mode 100644
index 0000000..1dce62a
--- /dev/null
+++ b/src/feature/core/invoice/domain/param/invoice-param.ts
@@ -0,0 +1,10 @@
+import { z } from "zod";
+
+export const invoiceSchema = z.object({
+  amount: z.coerce.number().gt(0, { message: 'Please enter an amount greater than $0.' }),
+  status: z.enum(['pending', 'paid'], {
+    invalid_type_error: 'Please select an invoice status.',
+  }),
+});
+
+export type InvoiceParam = z.infer<typeof invoiceSchema>
\ No newline at end of file
diff --git a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
new file mode 100644
index 0000000..1b3ea6a
--- /dev/null
+++ b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
@@ -0,0 +1,22 @@
+"use server"
+import serverDi from "@/feature/common/server-di";
+import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import { InvoiceParam, invoiceSchema } from "@/feature/core/invoice/domain/param/invoice-param";
+import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
+import { revalidatePath } from "next/cache";
+import { redirect } from "next/navigation";
+
+export default async function createInvoiceUsecase(params: InvoiceParam): Promise<string | {errorMessage: string}> {
+    const isParamsValid = invoiceSchema.safeParse(params)
+
+    if (!isParamsValid) {
+        return {
+            errorMessage: "Please pass correct params"
+        }
+    }
+    const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
+
+    revalidatePath("/dashboard")
+    return repo.createInvoice(params)
+
+}
\ No newline at end of file
-- 
2.39.5


From 324e7314e31f3203d2c73bfa5930b78493a9c326 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Sat, 2 Nov 2024 12:23:14 +0300
Subject: [PATCH 25/37] Add logic for loading handling with server action

---
 src/app/components/button/button-vm.ts        |  3 +-
 src/app/components/button/button.tsx          |  2 +-
 .../vm/create-random-invoice-button-vm.ts     | 18 ++++++---
 .../helpers/hooks/use-server-action.ts        | 40 +++++++++++++++++++
 .../core/invoice/data/repo/invoice-db-repo.ts |  2 +-
 .../domain/usecase/create-invoice-usecase.ts  |  3 --
 6 files changed, 56 insertions(+), 12 deletions(-)
 create mode 100644 src/bootstrap/helpers/hooks/use-server-action.ts

diff --git a/src/app/components/button/button-vm.ts b/src/app/components/button/button-vm.ts
index f9e8754..ef0d7af 100644
--- a/src/app/components/button/button-vm.ts
+++ b/src/app/components/button/button-vm.ts
@@ -1,6 +1,7 @@
 export default interface ButtonVm {
     props: {
-        title: string
+        title: string;
+        isDisable: boolean;
     }
     onClick(): void
 }
\ No newline at end of file
diff --git a/src/app/components/button/button.tsx b/src/app/components/button/button.tsx
index 5245285..df0de5b 100644
--- a/src/app/components/button/button.tsx
+++ b/src/app/components/button/button.tsx
@@ -11,7 +11,7 @@ export default class Button extends BaseView<ButtonVm> {
     protected Build(props: BuildProps<ButtonVm>): ReactNode {
         const {vm} = props
         
-        return <ButtonUi onClick={vm.onClick} >{vm.props.title}</ButtonUi> 
+        return <ButtonUi disabled={vm.props.isDisable} onClick={vm.onClick} >{vm.props.title}</ButtonUi> 
     }
 }
 
diff --git a/src/app/dashboard/vm/create-random-invoice-button-vm.ts b/src/app/dashboard/vm/create-random-invoice-button-vm.ts
index 11a258e..c5ceb13 100644
--- a/src/app/dashboard/vm/create-random-invoice-button-vm.ts
+++ b/src/app/dashboard/vm/create-random-invoice-button-vm.ts
@@ -1,10 +1,11 @@
 import ButtonVm from "@/app/components/button/button-vm";
+import { useServerAction } from "@/bootstrap/helpers/hooks/use-server-action";
 import useThrottle from "@/bootstrap/helpers/hooks/use-throttle";
 import BaseVM from "@/bootstrap/helpers/vm/base-vm";
 import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
 import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
 import { faker } from "@faker-js/faker";
-
+import { useRouter } from "next/navigation";
 export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
     private createInvoice: typeof createInvoiceUsecase
 
@@ -14,16 +15,20 @@ export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
     }
 
     useVM(): ButtonVm {
-        const throttledOnClick = useThrottle(this.onClickHandler.bind(this), 5000)
+        const router = useRouter()
+        const [action, isPending] = useServerAction(() => this.onClickHandler(router.refresh))
+        const throttledOnClick = useThrottle(action, 5000)
+
         return {
             props: {
-                title: "Create Random Invoice"
+                title: isPending ? "Loading" : "Create Random Invoice",
+                isDisable: isPending ? true : false
             },
-            onClick: throttledOnClick 
+            onClick: throttledOnClick.bind(this)
         }
     }
 
-    onClickHandler() {
+    async onClickHandler(refreshPage: () => void) {
         const fakedParams: InvoiceParam = {
             amount: faker.number.int({
                 min: 1,
@@ -31,6 +36,7 @@ export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
             }),
             status: "paid"
         }
-        this.createInvoice(fakedParams)
+        await this.createInvoice(fakedParams)
+        refreshPage()
     }
 } 
\ No newline at end of file
diff --git a/src/bootstrap/helpers/hooks/use-server-action.ts b/src/bootstrap/helpers/hooks/use-server-action.ts
new file mode 100644
index 0000000..abb7516
--- /dev/null
+++ b/src/bootstrap/helpers/hooks/use-server-action.ts
@@ -0,0 +1,40 @@
+import { useState, useEffect, useTransition, useRef } from 'react';
+
+/**
+ * 
+ * @param action Main server action to run
+ * @param onFinished Callback to run after action
+ * @returns transitioned action to run and is pending variable
+*/
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const useServerAction = <P extends any[], R>(
+  action: (...args: P) => Promise<R>,
+  onFinished?: (_: R | undefined) => void,
+): [(...args: P) => Promise<R | undefined>, boolean] => {
+  const [isPending, startTransition] = useTransition();
+  const [result, setResult] = useState<R>();
+  const [finished, setFinished] = useState(false);
+  const resolver = useRef<(value?: R | PromiseLike<R>) => void>();
+
+  useEffect(() => {
+    if (!finished) return;
+
+    if (onFinished) onFinished(result);
+    resolver.current?.(result);
+  }, [result, finished, onFinished]);
+
+  const runAction = async (...args: P): Promise<R | undefined> => {
+    startTransition(() => {
+      action(...args).then((data) => {
+        setResult(data);
+        setFinished(true);
+      });
+    });
+
+    return new Promise((resolve) => {
+      resolver.current = resolve;
+    });
+  };
+
+  return [runAction, isPending];
+};
\ No newline at end of file
diff --git a/src/feature/core/invoice/data/repo/invoice-db-repo.ts b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
index fd2219c..ea60b0f 100644
--- a/src/feature/core/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
@@ -16,7 +16,7 @@ export default class InvoiceDbRepo implements InvoiceRepo {
     async createInvoice(params: InvoiceParam): Promise<string> {
         const firstCustomerIdDb = await sql`SELECT 
             id FROM customers 
-            ORDER BY id ASC
+            ORDER BY id DESC 
             LIMIT 1
         `
         const customerId = firstCustomerIdDb.at(0)?.id
diff --git a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
index 1b3ea6a..73a0106 100644
--- a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
+++ b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
@@ -3,8 +3,6 @@ import serverDi from "@/feature/common/server-di";
 import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { InvoiceParam, invoiceSchema } from "@/feature/core/invoice/domain/param/invoice-param";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
-import { revalidatePath } from "next/cache";
-import { redirect } from "next/navigation";
 
 export default async function createInvoiceUsecase(params: InvoiceParam): Promise<string | {errorMessage: string}> {
     const isParamsValid = invoiceSchema.safeParse(params)
@@ -16,7 +14,6 @@ export default async function createInvoiceUsecase(params: InvoiceParam): Promis
     }
     const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
 
-    revalidatePath("/dashboard")
     return repo.createInvoice(params)
 
 }
\ No newline at end of file
-- 
2.39.5


From 0c70c66cf26be96f7ee8c45f280c267553d216ec Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Sat, 2 Nov 2024 12:44:40 +0300
Subject: [PATCH 26/37] Remove unused domain

---
 src/app/test/client/view/parent-view.tsx      | 13 -------
 src/app/test/client/vm/test-button-vm.ts      | 34 -------------------
 src/app/test/layout.tsx                       | 10 ------
 src/app/test/modules/test-app-module.ts       | 13 -------
 src/app/test/page.tsx                         |  5 ---
 src/feature/common/server-di.ts               |  3 --
 .../service/test-get-button-title-service.ts  |  9 -----
 .../domain/test/service/test-service-repo.ts  |  5 ---
 src/feature/domain/test/test-module-key.ts    |  1 -
 src/feature/infra/test/module/test-module.ts  | 12 -------
 src/feature/infra/test/repo/test-repo-iml.ts  | 14 --------
 11 files changed, 119 deletions(-)
 delete mode 100644 src/app/test/client/view/parent-view.tsx
 delete mode 100644 src/app/test/client/vm/test-button-vm.ts
 delete mode 100644 src/app/test/layout.tsx
 delete mode 100644 src/app/test/modules/test-app-module.ts
 delete mode 100644 src/app/test/page.tsx
 delete mode 100644 src/feature/domain/test/service/test-get-button-title-service.ts
 delete mode 100644 src/feature/domain/test/service/test-service-repo.ts
 delete mode 100644 src/feature/domain/test/test-module-key.ts
 delete mode 100644 src/feature/infra/test/module/test-module.ts
 delete mode 100644 src/feature/infra/test/repo/test-repo-iml.ts

diff --git a/src/app/test/client/view/parent-view.tsx b/src/app/test/client/view/parent-view.tsx
deleted file mode 100644
index 81de26a..0000000
--- a/src/app/test/client/view/parent-view.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-"use client"
-import Button from "@/app/components/button/button"
-import { TestButtonVM } from "@/app/test/client/vm/test-button-vm"
-import { useDI } from "@/bootstrap/di/di-context"
-import { useRef } from "react"
-
-export default function ParentView() {
-   const di = useDI()
-   
-    const vmRef = useRef(di.resolve(TestButtonVM))
-
-    return <Button vm={vmRef.current} />
-}
\ No newline at end of file
diff --git a/src/app/test/client/vm/test-button-vm.ts b/src/app/test/client/vm/test-button-vm.ts
deleted file mode 100644
index 1a74c04..0000000
--- a/src/app/test/client/vm/test-button-vm.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-"use client"
-import BaseVM from "@/bootstrap/helpers/vm/base-vm";
-import ButtonVm from "@/app/components/button/button-vm";
-import { useEffect, useState } from "react";
-import getButtonTitle from "@/feature/domain/test/service/test-get-button-title-service";
-
-export class TestButtonVM extends BaseVM<ButtonVm> {
-    private getButtonTitle: () => Promise<string>
-
-    constructor() {
-        super()
-        this.getButtonTitle = this.di.resolve(getButtonTitle.name)
-    }
-
-    useVM(): ButtonVm {
-        const [ buttonTitle, setTitle ] = useState("Default title")
-        useEffect(() => {
-            (async () => {
-                const title = await this.getButtonTitle()
-                setTitle(title)
-            })()
-        }, [])
-        return {
-            props: {
-                title: buttonTitle
-            },
-            onClick: () => {
-                console.log("clicked on the button");
-            }
-        }
-    }
-}
-
-export const testKey = "testKey"
\ No newline at end of file
diff --git a/src/app/test/layout.tsx b/src/app/test/layout.tsx
deleted file mode 100644
index 173e5ca..0000000
--- a/src/app/test/layout.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-"use client"
-import testAppModule from "@/app/test/modules/test-app-module";
-import { DiContext } from "@/bootstrap/di/di-context";
-import { PropsWithChildren, useRef } from "react";
-
-export default function WithDILayout(props: PropsWithChildren) {
-    const testDi = useRef(testAppModule())
-    return <DiContext.Provider value={testDi.current}>{props.children}</DiContext.Provider>
-}
-
diff --git a/src/app/test/modules/test-app-module.ts b/src/app/test/modules/test-app-module.ts
deleted file mode 100644
index 7f4d702..0000000
--- a/src/app/test/modules/test-app-module.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { TestButtonVM, testKey } from "@/app/test/client/vm/test-button-vm";
-import di from "@/bootstrap/di/init-di"
-import getButtonTitle from "@/feature/domain/test/service/test-get-button-title-service";
-
-export default function testAppModule() {
-    const testDi = di.createChildContainer()
-    
-    testDi.registerInstance(testKey, TestButtonVM);
-    testDi.register(getButtonTitle.name, {
-        useValue: getButtonTitle
-    })
-    return testDi
-}
diff --git a/src/app/test/page.tsx b/src/app/test/page.tsx
deleted file mode 100644
index fb067fd..0000000
--- a/src/app/test/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import ParentView from "@/app/test/client/view/parent-view";
-
-export default function Page() {
-    return <ParentView />
-}
\ No newline at end of file
diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
index 401e706..cec83f7 100644
--- a/src/feature/common/server-di.ts
+++ b/src/feature/common/server-di.ts
@@ -2,8 +2,6 @@ import getCustomerInvoiceDi from "@/feature/core/customer-invoice/data/module/cu
 import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
 import { customerKey } from "@/feature/core/customer/customer-key";
 import getCustomerDi from "@/feature/core/customer/data/module/customer-di";
-import { testModuleKey } from "@/feature/domain/test/test-module-key";
-import getTestModule from "@/feature/infra/test/module/test-module";
 import getInvoiceDi from "@/feature/core/invoice/data/module/invoice-di";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 import { DependencyContainer } from "tsyringe";
@@ -17,7 +15,6 @@ const memoizedDis: Record<string, DependencyContainer> = {}
 export default function serverDi(module: string): DependencyContainer {
     if (memoizedDis[module]) return memoizedDis[module]
     const getDi = {
-        [testModuleKey]: getTestModule,
         [customerKey]: getCustomerDi,
         [customerInvoiceModuleKey]: getCustomerInvoiceDi,
         [invoiceModuleKey]: getInvoiceDi,
diff --git a/src/feature/domain/test/service/test-get-button-title-service.ts b/src/feature/domain/test/service/test-get-button-title-service.ts
deleted file mode 100644
index c9a4910..0000000
--- a/src/feature/domain/test/service/test-get-button-title-service.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-"use server"
-import serverDi from "@/feature/common/server-di";
-import TestRepo, { testRepoKey } from "@/feature/domain/test/service/test-service-repo"
-import { testModuleKey } from "@/feature/domain/test/test-module-key";
-
-export default async function getButtonTitle() {
-    const repo = serverDi(testModuleKey).resolve<TestRepo>(testRepoKey) 
-    return repo.getButtonTitle()
-}
diff --git a/src/feature/domain/test/service/test-service-repo.ts b/src/feature/domain/test/service/test-service-repo.ts
deleted file mode 100644
index 91e00f8..0000000
--- a/src/feature/domain/test/service/test-service-repo.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export default interface TestRepo {
-    getButtonTitle(): Promise<string>
-}
-
-export const testRepoKey = "restRepoKey"
\ No newline at end of file
diff --git a/src/feature/domain/test/test-module-key.ts b/src/feature/domain/test/test-module-key.ts
deleted file mode 100644
index abb15e6..0000000
--- a/src/feature/domain/test/test-module-key.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const testModuleKey = "testModuleKey"
\ No newline at end of file
diff --git a/src/feature/infra/test/module/test-module.ts b/src/feature/infra/test/module/test-module.ts
deleted file mode 100644
index d8963b8..0000000
--- a/src/feature/infra/test/module/test-module.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import di from "@/bootstrap/di/init-di"
-import { testRepoKey } from "@/feature/domain/test/service/test-service-repo"
-import TestRepoImpl from "@/feature/infra/test/repo/test-repo-iml"
-
-export default function getTestModule() {
-    const testDi = di.createChildContainer()
-
-    di.register(testRepoKey, {
-        useClass: TestRepoImpl
-    })
-    return testDi
-}
\ No newline at end of file
diff --git a/src/feature/infra/test/repo/test-repo-iml.ts b/src/feature/infra/test/repo/test-repo-iml.ts
deleted file mode 100644
index f519bee..0000000
--- a/src/feature/infra/test/repo/test-repo-iml.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import TestRepo from "@/feature/domain/test/service/test-service-repo";
-
-export default class TestRepoImpl implements TestRepo {
-    async getButtonTitle(): Promise<string> {
-        await new Promise((res) => {
-            setTimeout(() => {
-                res(true)
-            }, 3000)
-        })
-        console.log('hereee');
-        return Promise.resolve("Button title")
-    }
-
-}
\ No newline at end of file
-- 
2.39.5


From 26833e18185944235825ba678aa5288558caf13d Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Sat, 2 Nov 2024 12:50:33 +0300
Subject: [PATCH 27/37] Fix location of db to boundaries

---
 package.json                                                    | 2 +-
 src/bootstrap/{ => boundaries}/db/db.ts                         | 2 +-
 src/bootstrap/{ => boundaries}/db/placeholder-data.js           | 0
 src/bootstrap/{ => boundaries}/db/seed.js                       | 0
 src/bootstrap/helpers/lib/actions.ts                            | 2 +-
 .../core/customer-invoice/data/repo/customer-invoice-db-repo.ts | 2 +-
 src/feature/core/customer/data/repo/customer-db-repo.ts         | 2 +-
 src/feature/core/invoice/data/repo/invoice-db-repo.ts           | 2 +-
 src/feature/core/revenue/data/repo/revenue-db-repo.ts           | 2 +-
 tsconfig.json                                                   | 2 +-
 10 files changed, 8 insertions(+), 8 deletions(-)
 rename src/bootstrap/{ => boundaries}/db/db.ts (86%)
 rename src/bootstrap/{ => boundaries}/db/placeholder-data.js (100%)
 rename src/bootstrap/{ => boundaries}/db/seed.js (100%)

diff --git a/package.json b/package.json
index 62211ce..8691324 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
     "start": "next start --port 4000",
     "lint": "next lint",
     "test": "vitest",
-    "seed": "node -r dotenv/config ./src/bootstrap/db/seed.js"
+    "seed": "node -r dotenv/config ./src/bootstrap/boundaries/db/seed.js"
   },
   "dependencies": {
     "@heroicons/react": "^2.1.5",
diff --git a/src/bootstrap/db/db.ts b/src/bootstrap/boundaries/db/db.ts
similarity index 86%
rename from src/bootstrap/db/db.ts
rename to src/bootstrap/boundaries/db/db.ts
index dbcd877..d6962af 100644
--- a/src/bootstrap/db/db.ts
+++ b/src/bootstrap/boundaries/db/db.ts
@@ -10,4 +10,4 @@ const dbConfigs = {
     database: envs.POSTGRES_DB,
 }
 
-export const sql = postgres(dbConfigs);
\ No newline at end of file
+export const sql = postgres(dbConfigs);
diff --git a/src/bootstrap/db/placeholder-data.js b/src/bootstrap/boundaries/db/placeholder-data.js
similarity index 100%
rename from src/bootstrap/db/placeholder-data.js
rename to src/bootstrap/boundaries/db/placeholder-data.js
diff --git a/src/bootstrap/db/seed.js b/src/bootstrap/boundaries/db/seed.js
similarity index 100%
rename from src/bootstrap/db/seed.js
rename to src/bootstrap/boundaries/db/seed.js
diff --git a/src/bootstrap/helpers/lib/actions.ts b/src/bootstrap/helpers/lib/actions.ts
index 617b4d7..a408241 100644
--- a/src/bootstrap/helpers/lib/actions.ts
+++ b/src/bootstrap/helpers/lib/actions.ts
@@ -3,7 +3,7 @@
 import { z } from 'zod';
 import { revalidatePath } from 'next/cache';
 import { redirect } from 'next/navigation';
-import { sql } from '@/bootstrap/db/db';
+import { sql } from '@/bootstrap/boundaries/db/db';
 
 const FormSchema = z.object({
   id: z.string(),
diff --git a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
index 9c19809..a12de93 100644
--- a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
+++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -1,4 +1,4 @@
-import { sql } from "@/bootstrap/db/db";
+import { sql } from "@/bootstrap/boundaries/db/db";
 import { formatCurrency } from "@/feature/common/feature-helpers";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 import CustomerInvoiceRepo from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
diff --git a/src/feature/core/customer/data/repo/customer-db-repo.ts b/src/feature/core/customer/data/repo/customer-db-repo.ts
index f1239a4..8c6fba4 100644
--- a/src/feature/core/customer/data/repo/customer-db-repo.ts
+++ b/src/feature/core/customer/data/repo/customer-db-repo.ts
@@ -1,4 +1,4 @@
-import { sql } from "@/bootstrap/db/db";
+import { sql } from "@/bootstrap/boundaries/db/db";
 import { formatCurrency } from "@/feature/common/feature-helpers";
 import Customer from "@/feature/core/customer/domain/entity/customer";
 import CustomerRepo from "@/feature/core/customer/domain/i-repo/customer-repo";
diff --git a/src/feature/core/invoice/data/repo/invoice-db-repo.ts b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
index ea60b0f..c8e2bb9 100644
--- a/src/feature/core/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
@@ -1,4 +1,4 @@
-import { sql } from "@/bootstrap/db/db";
+import { sql } from "@/bootstrap/boundaries/db/db";
 import { formatCurrency } from "@/feature/common/feature-helpers";
 import InvoiceRepo from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
diff --git a/src/feature/core/revenue/data/repo/revenue-db-repo.ts b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
index 92ac993..d8d771d 100644
--- a/src/feature/core/revenue/data/repo/revenue-db-repo.ts
+++ b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
@@ -1,4 +1,4 @@
-import { sql } from "@/bootstrap/db/db";
+import { sql } from "@/bootstrap/boundaries/db/db";
 import Revenue from "@/feature/core/revenue/domain/entity/revenue";
 import RevenueRepo from "@/feature/core/revenue/domain/i-repo/revenue-repo";
 import { connection } from "next/server";
diff --git a/tsconfig.json b/tsconfig.json
index ef29509..0d0b9fb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -25,6 +25,6 @@
       "@/*": ["./src/*"]
     }
   },
-  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/bootstrap/db/seed.js", "src/bootstrap/db/placeholder-data.js"],
+  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/bootstrap/boundaries/db/seed.js", "src/bootstrap/boundaries/db/placeholder-data.js"],
   "exclude": ["node_modules"]
 }
-- 
2.39.5


From 4c4b00c51fb4d4a180aa4ea02bd89362760275c1 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Sat, 2 Nov 2024 13:10:17 +0300
Subject: [PATCH 28/37] Add tools for failure handling

---
 package.json                                  |  1 +
 src/feature/common/data/api-task.ts           |  8 ++
 src/feature/common/failures/base-failure.ts   | 22 +++++
 .../common/failures/dev/arguments-failure.ts  | 12 +++
 .../common/failures/dev/base-dev-failure.ts   |  3 +
 .../common/failures/dev/dependency-failure.ts | 10 ++
 .../common/failures/failure-helpers.ts        | 95 +++++++++++++++++++
 .../common/failures/network-failure.ts        | 12 +++
 yarn.lock                                     |  5 +
 9 files changed, 168 insertions(+)
 create mode 100644 src/feature/common/data/api-task.ts
 create mode 100644 src/feature/common/failures/base-failure.ts
 create mode 100644 src/feature/common/failures/dev/arguments-failure.ts
 create mode 100644 src/feature/common/failures/dev/base-dev-failure.ts
 create mode 100644 src/feature/common/failures/dev/dependency-failure.ts
 create mode 100644 src/feature/common/failures/failure-helpers.ts
 create mode 100644 src/feature/common/failures/network-failure.ts

diff --git a/package.json b/package.json
index 8691324..6fd58e3 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
     "@radix-ui/react-slot": "^1.1.0",
     "class-variance-authority": "^0.7.0",
     "clsx": "^2.1.1",
+    "fp-ts": "^2.16.9",
     "lucide-react": "^0.454.0",
     "next": "15.0.2",
     "postgres": "^3.4.5",
diff --git a/src/feature/common/data/api-task.ts b/src/feature/common/data/api-task.ts
new file mode 100644
index 0000000..f234898
--- /dev/null
+++ b/src/feature/common/data/api-task.ts
@@ -0,0 +1,8 @@
+import { Either } from "fp-ts/lib/Either";
+import { TaskEither } from "fp-ts/lib/TaskEither";
+import BaseFailure from "@/feature/common/failures/base-failure";
+
+type ApiTask<ResponseType> = TaskEither<BaseFailure, ResponseType>;
+export type ApiEither<ResponseType> = Either<BaseFailure, ResponseType>;
+
+export default ApiTask;
diff --git a/src/feature/common/failures/base-failure.ts b/src/feature/common/failures/base-failure.ts
new file mode 100644
index 0000000..bc09402
--- /dev/null
+++ b/src/feature/common/failures/base-failure.ts
@@ -0,0 +1,22 @@
+import { makeFailureMessage } from "@/feature/common/failures/failure-helpers";
+
+/**
+ * This is a class called  BaseFailure  that extends the  Error  class. It is
+ *  used as a base class for creating custom failure classes.
+ */
+export default abstract class BaseFailure {
+  /* ------------------------------- Attributes ------------------------------- */
+  private readonly BASE_FAILURE_MESSAGE = "failure";
+
+  /* -------------------------------------------------------------------------- */
+  /**
+   * Use this message as key lang for failure messages
+   */
+  message = this.BASE_FAILURE_MESSAGE;
+
+  /* -------------------------------------------------------------------------- */
+  constructor(key: string) {
+    this.message = makeFailureMessage(this.message, key);
+  }
+  /* -------------------------------------------------------------------------- */
+}
diff --git a/src/feature/common/failures/dev/arguments-failure.ts b/src/feature/common/failures/dev/arguments-failure.ts
new file mode 100644
index 0000000..ed4e2c9
--- /dev/null
+++ b/src/feature/common/failures/dev/arguments-failure.ts
@@ -0,0 +1,12 @@
+import BaseDevFailure from "@/feature/common/failures/dev/base-dev-failure";
+
+/**
+ * Failure for needed arguments in a method but sent wrong one
+ */
+export default class ArgumentsFailure extends BaseDevFailure {
+  /* ------------------------------- Constructor ------------------------------ */
+  constructor() {
+    super("arguments");
+  }
+  /* -------------------------------------------------------------------------- */
+}
diff --git a/src/feature/common/failures/dev/base-dev-failure.ts b/src/feature/common/failures/dev/base-dev-failure.ts
new file mode 100644
index 0000000..aaf8142
--- /dev/null
+++ b/src/feature/common/failures/dev/base-dev-failure.ts
@@ -0,0 +1,3 @@
+import BaseFailure from "@/feature/common/failures/base-failure";
+
+export default abstract class BaseDevFailure extends BaseFailure {}
diff --git a/src/feature/common/failures/dev/dependency-failure.ts b/src/feature/common/failures/dev/dependency-failure.ts
new file mode 100644
index 0000000..86b49cf
--- /dev/null
+++ b/src/feature/common/failures/dev/dependency-failure.ts
@@ -0,0 +1,10 @@
+import BaseDevFailure from "@/feature/common/failures/dev/base-dev-failure";
+
+/**
+ * This is a failure of not having specific dependency
+ */
+export default class DependencyFailure extends BaseDevFailure {
+  constructor() {
+    super("DependencyFailure");
+  }
+}
diff --git a/src/feature/common/failures/failure-helpers.ts b/src/feature/common/failures/failure-helpers.ts
new file mode 100644
index 0000000..a6c8f46
--- /dev/null
+++ b/src/feature/common/failures/failure-helpers.ts
@@ -0,0 +1,95 @@
+import BaseFailure from "@/feature/common/failures/base-failure";
+
+/**
+ * This method is supposed to save previous failure of TaskEither
+ * to prevent it from loosing and overriding by the new one.
+ *
+ * Usage example:
+ * ```ts
+ * tryCatch(
+ *  async () => {
+ *   ...
+ *   throw ValidationFailure();
+ *   ...
+ *  },
+ *  (reason) => failureOr(reason, new UserCreationFailure()),
+ * )
+ * ```
+ * In this example `failureOr` will return already throwed
+ * instance of `BaseFailure` which is `ValidationFailure`.
+ *
+ *
+ * @param reason is throwed object.
+ * Basically it can be default `Error` or instance of `BaseFailure`.
+ * @param failure instance of `BaseFailure` that will be returned
+ * if reason is not instance of `BaseFailure`.
+ * @returns `BaseFailure`
+ */
+export function failureOr(reason: unknown, failure: BaseFailure): BaseFailure {
+  if (reason instanceof BaseFailure) {
+    return reason;
+  }
+  return failure;
+}
+
+/**
+ * Returns a function that maps a BaseFailure instance to a new BaseFailure instance of type IfType using the provided mapping function.
+ * @param f A function that maps an instance of IfType to a new instance of BaseFailure.
+ * @param ctor A constructor function for IfType.
+ * @returns A function that maps a BaseFailure instance to a new BaseFailure instance of type IfType.
+ */
+export function mapToFailureFrom<IfType extends BaseFailure>(
+  f: (t: IfType) => BaseFailure,
+  ctor: new (...args: never[]) => IfType,
+): (t: BaseFailure) => BaseFailure {
+  return mapIfInstance<IfType, BaseFailure>(f, ctor);
+}
+
+/**
+ * Maps an instance of a class to a response using a provided function.
+ *
+ * @template IfType - The type of the instance to map.
+ * @template Response - The type of the response to map to.
+ * @param {function} f - The function to use to map the instance to a response.
+ * @param {new (...args: never[]) => IfType} ctor - The constructor function of the instance to map.
+ * @returns {(t: IfType | Response) => IfType | Response} - A function that maps the instance to a response using the provided function.
+ */
+export function mapIfInstance<IfType, Response>(
+  f: (t: IfType) => Response,
+  ctor: new (...args: never[]) => IfType,
+) {
+  return (t: IfType | Response) => {
+    if (t instanceof ctor) {
+      return f(t);
+    }
+    return t;
+  };
+}
+
+/**
+ * Maps a function to a value if it is not an instance of a given class.
+ * @template IfType The type of the value to be mapped.
+ * @template Response The type of the mapped value.
+ * @param {function} f The function to map the value with.
+ * @param {new (...args: never[]) => IfType} ctor The class to check the value against.
+ * @returns {function} A function that maps the value if it is not an instance of the given class.
+ */
+export function mapIfNotInstance<IfType, Response>(
+  f: (t: IfType) => Response,
+  ctor: new (...args: never[]) => IfType,
+) {
+  return (t: IfType | Response) => {
+    if (t! instanceof ctor) {
+      return f(t);
+    }
+    return t;
+  };
+}
+
+/**
+ * Gets Message key and it'll add it to the failure message key hierarchy
+ */
+export function makeFailureMessage(message: string, key: string) {
+  if (!key) return message;
+  return `${message}.${key}`;
+}
diff --git a/src/feature/common/failures/network-failure.ts b/src/feature/common/failures/network-failure.ts
new file mode 100644
index 0000000..3268fc3
--- /dev/null
+++ b/src/feature/common/failures/network-failure.ts
@@ -0,0 +1,12 @@
+import BaseFailure from "./base-failure";
+
+/**
+ * Failure for HTTP response when response dosn't have base structure
+ */
+export default class NetworkFailure extends BaseFailure {
+  /* ------------------------------- Constructor ------------------------------ */
+  constructor() {
+    super("network");
+  }
+  /* -------------------------------------------------------------------------- */
+}
diff --git a/yarn.lock b/yarn.lock
index 6d7dde4..194f43b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2125,6 +2125,11 @@ form-data@^4.0.0:
     combined-stream "^1.0.8"
     mime-types "^2.1.12"
 
+fp-ts@^2.16.9:
+  version "2.16.9"
+  resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.16.9.tgz#99628fc5e0bb3b432c4a16d8f4455247380bae8a"
+  integrity sha512-+I2+FnVB+tVaxcYyQkHUq7ZdKScaBlX53A41mxQtpIccsfyv8PzdzP7fzp2AY832T4aoK6UZ5WRX/ebGd8uZuQ==
+
 fs-minipass@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
-- 
2.39.5


From 7046c5adc99ecf90a0ae89e1e4cdeae35a8a74fb Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Sat, 2 Nov 2024 14:57:23 +0300
Subject: [PATCH 29/37] Add demo logics for using fp-ts in feature layer

---
 .../latest-invoices-controller.ts             |  4 +-
 .../latest-invoices/latest-invoices.tsx       |  5 ++-
 src/feature/common/failures/params-failure.ts | 12 ++++++
 .../data/repo/customer-invoice-db-repo.ts     | 37 +++++++++++--------
 .../domain/i-repo/customer-invoice-repo.ts    |  3 +-
 .../fetch-customer-invoices-usecase.ts        |  6 +--
 .../customer/domain/i-repo/customer-repo.ts   |  3 +-
 .../invoice/domain/i-repo/invoice-repo.ts     |  3 +-
 .../domain/usecase/create-invoice-usecase.ts  | 25 ++++++++-----
 9 files changed, 64 insertions(+), 34 deletions(-)
 create mode 100644 src/feature/common/failures/params-failure.ts

diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts b/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
index 45bde22..8fbbde9 100644
--- a/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
+++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
@@ -1,5 +1,5 @@
 import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
 
-export default function latestInvoicesController() {
-    return fetchCustomerInvoicesUsecase()
+export default async function latestInvoicesController() {
+    return await fetchCustomerInvoicesUsecase()
 }
\ No newline at end of file
diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
index 09396cd..696af73 100644
--- a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
+++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
@@ -2,12 +2,15 @@ import CreateRandomInvoiceContainer from '@/app/dashboard/components/client/crea
 import latestInvoicesController from '@/app/dashboard/components/server/latest-invoices/latest-invoices-controller';
 import { ArrowPathIcon } from '@heroicons/react/24/outline';
 import clsx from 'clsx';
+import { isLeft } from 'fp-ts/lib/Either';
 import Image from 'next/image';
 
 export default async function LatestInvoices() {
   const latestInvoices = await latestInvoicesController();
 
-  const invoices = latestInvoices.map((invoice, i) => {
+  if (isLeft(latestInvoices)) return <div>Error</div>
+  
+  const invoices = latestInvoices.right.map((invoice, i) => {
             return (
               <div
                 key={invoice.id}
diff --git a/src/feature/common/failures/params-failure.ts b/src/feature/common/failures/params-failure.ts
new file mode 100644
index 0000000..4849027
--- /dev/null
+++ b/src/feature/common/failures/params-failure.ts
@@ -0,0 +1,12 @@
+import BaseFailure from "./base-failure";
+
+/**
+ * Failure for params failure 
+ */
+export default class ParamsFailure extends BaseFailure {
+  /* ------------------------------- Constructor ------------------------------ */
+  constructor() {
+    super("params");
+  }
+  /* -------------------------------------------------------------------------- */
+}
diff --git a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
index a12de93..db036b8 100644
--- a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
+++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -1,7 +1,12 @@
 import { sql } from "@/bootstrap/boundaries/db/db";
+import ApiTask from "@/feature/common/data/api-task";
+import { failureOr } from "@/feature/common/failures/failure-helpers";
+import NetworkFailure from "@/feature/common/failures/network-failure";
 import { formatCurrency } from "@/feature/common/feature-helpers";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 import CustomerInvoiceRepo from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
+import { pipe } from "fp-ts/lib/function";
+import { tryCatch } from "fp-ts/lib/TaskEither";
 import postgres from "postgres";
 
 type customerInvoiceDbResponse = {
@@ -13,23 +18,25 @@ type customerInvoiceDbResponse = {
 }
 
 export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
-    async fetchList(): Promise<CustomerInvoice[]> {
-        try {
-            const data = await sql`
-            SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
-            FROM invoices
-            JOIN customers ON invoices.customer_id = customers.id
-            ORDER BY invoices.date DESC
-            LIMIT 20 ` as postgres.RowList<customerInvoiceDbResponse[]>;
-
-            return this.customerInvoicesDto(data)
-        } catch (error) {
-            console.error('Database Error:', error);
-            throw new Error('Failed to fetch the latest invoices.');
-        }
+    fetchList(): ApiTask<CustomerInvoice[]> {
+        
+        return pipe(
+            tryCatch(
+                async () => {
+                    const response = await sql`
+                    SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
+                    FROM invoices
+                    JOIN customers ON invoices.customer_id = customers.id
+                    ORDER BY invoices.date DESC
+                    LIMIT 20 ` as postgres.RowList<customerInvoiceDbResponse[]>;
+                    
+                    return  this.customerInvoicesDto(response)
+                },
+                (l) => failureOr(l, new NetworkFailure())
+            )
+        )
     }
 
-
     private customerInvoicesDto(dbCustomers: customerInvoiceDbResponse[]): CustomerInvoice[] {
         return  dbCustomers.map((customer) => this.customerInvoiceDto(customer));
     }
diff --git a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
index 0a8c993..ab4b486 100644
--- a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
+++ b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
@@ -1,7 +1,8 @@
+import ApiTask from "@/feature/common/data/api-task"
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice"
 
 export default interface CustomerInvoiceRepo {
-    fetchList(): Promise<CustomerInvoice[]>
+    fetchList(): ApiTask<CustomerInvoice[]>
 }
 
 export const customerInvoiceRepoKey = "customerInvoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
index 5947553..5d2df55 100644
--- a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
+++ b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
@@ -1,12 +1,12 @@
-"use server"
+import { ApiEither } from "@/feature/common/data/api-task";
 import serverDi from "@/feature/common/server-di";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
 import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
 import { connection } from "next/server";
 
-export default async function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> {
+export default async function fetchCustomerInvoicesUsecase(): Promise<ApiEither<CustomerInvoice[]>> {
     connection()
     const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey)
-    return repo.fetchList()
+    return repo.fetchList()()
 }
\ No newline at end of file
diff --git a/src/feature/core/customer/domain/i-repo/customer-repo.ts b/src/feature/core/customer/domain/i-repo/customer-repo.ts
index d48fdde..86b890e 100644
--- a/src/feature/core/customer/domain/i-repo/customer-repo.ts
+++ b/src/feature/core/customer/domain/i-repo/customer-repo.ts
@@ -1,7 +1,8 @@
+import ApiTask from "@/feature/common/data/api-task"
 import Customer from "@/feature/core/customer/domain/entity/customer"
 
 export default interface CustomerRepo {
-    fetchList(query: string): Promise<Customer[]>
+    fetchList(query: string): ApiTask<Customer[]>
     fetchCustomersAmount(): Promise<number>
 }
 
diff --git a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
index 983ac3f..ca5096d 100644
--- a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
+++ b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
@@ -1,10 +1,11 @@
+import ApiTask from "@/feature/common/data/api-task"
 import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param"
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status"
 
 export default interface InvoiceRepo {
     fetchAllInvoicesAmount(): Promise<number>
     fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary>
-    createInvoice(params: InvoiceParam): Promise<string>
+    createInvoice(params: InvoiceParam): ApiTask<string>
 }
 
 export const invoiceRepoKey = "invoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
index 73a0106..9cfc63d 100644
--- a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
+++ b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
@@ -1,19 +1,24 @@
 "use server"
+import { ApiEither } from "@/feature/common/data/api-task";
+import ParamsFailure from "@/feature/common/failures/params-failure";
 import serverDi from "@/feature/common/server-di";
 import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { InvoiceParam, invoiceSchema } from "@/feature/core/invoice/domain/param/invoice-param";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
+import { pipe } from "fp-ts/lib/function";
+import { chain, fromNullable, left, map, right } from "fp-ts/lib/TaskEither";
 
-export default async function createInvoiceUsecase(params: InvoiceParam): Promise<string | {errorMessage: string}> {
-    const isParamsValid = invoiceSchema.safeParse(params)
-
-    if (!isParamsValid) {
-        return {
-            errorMessage: "Please pass correct params"
-        }
-    }
+export default async function createInvoiceUsecase(params: InvoiceParam): Promise<ApiEither<string>> {
     const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
 
-    return repo.createInvoice(params)
-
+    return pipe(
+        fromNullable(new ParamsFailure())(params),
+        map((params) => invoiceSchema.safeParse(params)),
+        chain((params) => {
+            const isParamsValid = invoiceSchema.safeParse(params)
+            if (!isParamsValid.success) left(new ParamsFailure())
+            return right(params.data as InvoiceParam)
+        }),
+        chain((params) => repo.createInvoice(params))
+    )()
 }
\ No newline at end of file
-- 
2.39.5


From 6f54fa0c67c4a124c383ff590b10d70e74350409 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Sat, 2 Nov 2024 14:57:23 +0300
Subject: [PATCH 30/37] Add demo logics for using fp-ts in feature layer

---
 .../latest-invoices-controller.ts             |  4 +-
 .../latest-invoices/latest-invoices.tsx       |  5 +-
 src/feature/common/failures/base-failure.ts   |  8 ++-
 .../common/failures/dev/arguments-failure.ts  |  6 +--
 .../common/failures/dev/base-dev-failure.ts   |  2 +-
 .../common/failures/dev/dependency-failure.ts |  6 +--
 .../common/failures/network-failure.ts        |  6 +--
 src/feature/common/failures/params-failure.ts | 12 +++++
 .../data/repo/customer-invoice-db-repo.ts     | 36 +++++++------
 .../domain/i-repo/customer-invoice-repo.ts    |  3 +-
 .../fetch-customer-invoices-usecase.ts        |  6 +--
 .../core/invoice/data/repo/invoice-db-repo.ts | 52 ++++++++++++-------
 .../invoice/domain/i-repo/invoice-repo.ts     |  3 +-
 .../domain/usecase/create-invoice-usecase.ts  | 25 +++++----
 14 files changed, 109 insertions(+), 65 deletions(-)
 create mode 100644 src/feature/common/failures/params-failure.ts

diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts b/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
index 45bde22..8fbbde9 100644
--- a/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
+++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
@@ -1,5 +1,5 @@
 import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
 
-export default function latestInvoicesController() {
-    return fetchCustomerInvoicesUsecase()
+export default async function latestInvoicesController() {
+    return await fetchCustomerInvoicesUsecase()
 }
\ No newline at end of file
diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
index 09396cd..696af73 100644
--- a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
+++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
@@ -2,12 +2,15 @@ import CreateRandomInvoiceContainer from '@/app/dashboard/components/client/crea
 import latestInvoicesController from '@/app/dashboard/components/server/latest-invoices/latest-invoices-controller';
 import { ArrowPathIcon } from '@heroicons/react/24/outline';
 import clsx from 'clsx';
+import { isLeft } from 'fp-ts/lib/Either';
 import Image from 'next/image';
 
 export default async function LatestInvoices() {
   const latestInvoices = await latestInvoicesController();
 
-  const invoices = latestInvoices.map((invoice, i) => {
+  if (isLeft(latestInvoices)) return <div>Error</div>
+  
+  const invoices = latestInvoices.right.map((invoice, i) => {
             return (
               <div
                 key={invoice.id}
diff --git a/src/feature/common/failures/base-failure.ts b/src/feature/common/failures/base-failure.ts
index bc09402..43e6117 100644
--- a/src/feature/common/failures/base-failure.ts
+++ b/src/feature/common/failures/base-failure.ts
@@ -4,7 +4,7 @@ import { makeFailureMessage } from "@/feature/common/failures/failure-helpers";
  * This is a class called  BaseFailure  that extends the  Error  class. It is
  *  used as a base class for creating custom failure classes.
  */
-export default abstract class BaseFailure {
+export default abstract class BaseFailure<META_DATA> {
   /* ------------------------------- Attributes ------------------------------- */
   private readonly BASE_FAILURE_MESSAGE = "failure";
 
@@ -15,8 +15,12 @@ export default abstract class BaseFailure {
   message = this.BASE_FAILURE_MESSAGE;
 
   /* -------------------------------------------------------------------------- */
-  constructor(key: string) {
+  metadata: META_DATA | undefined; 
+
+  /* -------------------------------------------------------------------------- */
+  constructor(key: string, metadata?: META_DATA) {
     this.message = makeFailureMessage(this.message, key);
+    this.metadata = metadata ?? undefined
   }
   /* -------------------------------------------------------------------------- */
 }
diff --git a/src/feature/common/failures/dev/arguments-failure.ts b/src/feature/common/failures/dev/arguments-failure.ts
index ed4e2c9..11d01c2 100644
--- a/src/feature/common/failures/dev/arguments-failure.ts
+++ b/src/feature/common/failures/dev/arguments-failure.ts
@@ -3,10 +3,10 @@ import BaseDevFailure from "@/feature/common/failures/dev/base-dev-failure";
 /**
  * Failure for needed arguments in a method but sent wrong one
  */
-export default class ArgumentsFailure extends BaseDevFailure {
+export default class ArgumentsFailure<META_DATA> extends BaseDevFailure<META_DATA> {
   /* ------------------------------- Constructor ------------------------------ */
-  constructor() {
-    super("arguments");
+  constructor(metadata?: META_DATA) {
+    super("arguments", metadata);
   }
   /* -------------------------------------------------------------------------- */
 }
diff --git a/src/feature/common/failures/dev/base-dev-failure.ts b/src/feature/common/failures/dev/base-dev-failure.ts
index aaf8142..ad404d1 100644
--- a/src/feature/common/failures/dev/base-dev-failure.ts
+++ b/src/feature/common/failures/dev/base-dev-failure.ts
@@ -1,3 +1,3 @@
 import BaseFailure from "@/feature/common/failures/base-failure";
 
-export default abstract class BaseDevFailure extends BaseFailure {}
+export default abstract class BaseDevFailure<META_DATA> extends BaseFailure<META_DATA> {}
diff --git a/src/feature/common/failures/dev/dependency-failure.ts b/src/feature/common/failures/dev/dependency-failure.ts
index 86b49cf..6f16f8f 100644
--- a/src/feature/common/failures/dev/dependency-failure.ts
+++ b/src/feature/common/failures/dev/dependency-failure.ts
@@ -3,8 +3,8 @@ import BaseDevFailure from "@/feature/common/failures/dev/base-dev-failure";
 /**
  * This is a failure of not having specific dependency
  */
-export default class DependencyFailure extends BaseDevFailure {
-  constructor() {
-    super("DependencyFailure");
+export default class DependencyFailure<META_DATA> extends BaseDevFailure<META_DATA> {
+  constructor(metadata: META_DATA) {
+    super("DependencyFailure", metadata);
   }
 }
diff --git a/src/feature/common/failures/network-failure.ts b/src/feature/common/failures/network-failure.ts
index 3268fc3..192bdf5 100644
--- a/src/feature/common/failures/network-failure.ts
+++ b/src/feature/common/failures/network-failure.ts
@@ -3,10 +3,10 @@ import BaseFailure from "./base-failure";
 /**
  * Failure for HTTP response when response dosn't have base structure
  */
-export default class NetworkFailure extends BaseFailure {
+export default class NetworkFailure<META_DATA> extends BaseFailure<META_DATA> {
   /* ------------------------------- Constructor ------------------------------ */
-  constructor() {
-    super("network");
+  constructor(metaData?: META_DATA) {
+    super("network", metaData);
   }
   /* -------------------------------------------------------------------------- */
 }
diff --git a/src/feature/common/failures/params-failure.ts b/src/feature/common/failures/params-failure.ts
new file mode 100644
index 0000000..df50d8c
--- /dev/null
+++ b/src/feature/common/failures/params-failure.ts
@@ -0,0 +1,12 @@
+import BaseFailure from "./base-failure";
+
+/**
+ * Failure for params failure 
+ */
+export default class ParamsFailure<META_DATA> extends BaseFailure<META_DATA> {
+  /* ------------------------------- Constructor ------------------------------ */
+  constructor(metadata?: META_DATA) {
+    super("params", metadata);
+  }
+  /* -------------------------------------------------------------------------- */
+}
diff --git a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
index a12de93..bc35087 100644
--- a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
+++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -1,7 +1,12 @@
 import { sql } from "@/bootstrap/boundaries/db/db";
+import ApiTask from "@/feature/common/data/api-task";
+import { failureOr } from "@/feature/common/failures/failure-helpers";
+import NetworkFailure from "@/feature/common/failures/network-failure";
 import { formatCurrency } from "@/feature/common/feature-helpers";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 import CustomerInvoiceRepo from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
+import { pipe } from "fp-ts/lib/function";
+import { tryCatch } from "fp-ts/lib/TaskEither";
 import postgres from "postgres";
 
 type customerInvoiceDbResponse = {
@@ -13,23 +18,24 @@ type customerInvoiceDbResponse = {
 }
 
 export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
-    async fetchList(): Promise<CustomerInvoice[]> {
-        try {
-            const data = await sql`
-            SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
-            FROM invoices
-            JOIN customers ON invoices.customer_id = customers.id
-            ORDER BY invoices.date DESC
-            LIMIT 20 ` as postgres.RowList<customerInvoiceDbResponse[]>;
-
-            return this.customerInvoicesDto(data)
-        } catch (error) {
-            console.error('Database Error:', error);
-            throw new Error('Failed to fetch the latest invoices.');
-        }
+    fetchList(): ApiTask<CustomerInvoice[]> {
+        return pipe(
+            tryCatch(
+                async () => {
+                    const response = await sql`
+                    SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
+                    FROM invoices
+                    JOIN customers ON invoices.customer_id = customers.id
+                    ORDER BY invoices.date DESC
+                    LIMIT 20 ` as postgres.RowList<customerInvoiceDbResponse[]>;
+                    
+                    return  this.customerInvoicesDto(response)
+                },
+                (l) => failureOr(l, new NetworkFailure())
+            )
+        )
     }
 
-
     private customerInvoicesDto(dbCustomers: customerInvoiceDbResponse[]): CustomerInvoice[] {
         return  dbCustomers.map((customer) => this.customerInvoiceDto(customer));
     }
diff --git a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
index 0a8c993..ab4b486 100644
--- a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
+++ b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
@@ -1,7 +1,8 @@
+import ApiTask from "@/feature/common/data/api-task"
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice"
 
 export default interface CustomerInvoiceRepo {
-    fetchList(): Promise<CustomerInvoice[]>
+    fetchList(): ApiTask<CustomerInvoice[]>
 }
 
 export const customerInvoiceRepoKey = "customerInvoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
index 5947553..5d2df55 100644
--- a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
+++ b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
@@ -1,12 +1,12 @@
-"use server"
+import { ApiEither } from "@/feature/common/data/api-task";
 import serverDi from "@/feature/common/server-di";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
 import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
 import { connection } from "next/server";
 
-export default async function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> {
+export default async function fetchCustomerInvoicesUsecase(): Promise<ApiEither<CustomerInvoice[]>> {
     connection()
     const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey)
-    return repo.fetchList()
+    return repo.fetchList()()
 }
\ No newline at end of file
diff --git a/src/feature/core/invoice/data/repo/invoice-db-repo.ts b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
index c8e2bb9..416c060 100644
--- a/src/feature/core/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
@@ -1,8 +1,13 @@
 import { sql } from "@/bootstrap/boundaries/db/db";
+import ApiTask from "@/feature/common/data/api-task";
+import { failureOr } from "@/feature/common/failures/failure-helpers";
+import NetworkFailure from "@/feature/common/failures/network-failure";
 import { formatCurrency } from "@/feature/common/feature-helpers";
 import InvoiceRepo from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
+import { pipe } from "fp-ts/lib/function";
+import { tryCatch } from "fp-ts/lib/TaskEither";
 import postgres from "postgres";
 
 type InvoiceSummaryDbResponse = {paid: string, pending: string}
@@ -13,26 +18,33 @@ export default class InvoiceDbRepo implements InvoiceRepo {
         return data.count ?? 0
     }
 
-    async createInvoice(params: InvoiceParam): Promise<string> {
-        const firstCustomerIdDb = await sql`SELECT 
-            id FROM customers 
-            ORDER BY id DESC 
-            LIMIT 1
-        `
-        const customerId = firstCustomerIdDb.at(0)?.id
-        if (!customerId) throw new Error("There is no customer")
-        
-        const { amount, status } = params;
-        const amountInCents = amount * 100;
-        const date = new Date().toISOString().split('T')[0];
-
-        // Insert data into the database
-        const result = await sql`
-            INSERT INTO invoices (customer_id, amount, status, date)
-            VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
-            RETURNING id
-        `;
-        return result.at(0)?.id ?? ""
+    createInvoice(params: InvoiceParam): ApiTask<string> {
+        return pipe(
+            tryCatch(
+                async () => {
+                    const firstCustomerIdDb = await sql`SELECT 
+                        id FROM customers 
+                        ORDER BY id DESC 
+                        LIMIT 1
+                    `
+                    const customerId = firstCustomerIdDb.at(0)?.id
+                    if (!customerId) throw new Error("There is no customer")
+                    
+                    const { amount, status } = params;
+                    const amountInCents = amount * 100;
+                    const date = new Date().toISOString().split('T')[0];
+            
+                    // Insert data into the database
+                    const result = await sql`
+                        INSERT INTO invoices (customer_id, amount, status, date)
+                        VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
+                        RETURNING id
+                    `;
+                    return result.at(0)?.id ?? ""
+                },
+                (l) => failureOr(l, new NetworkFailure(l as Error))
+            ),
+        )
     }
 
     async fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
diff --git a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
index 983ac3f..ca5096d 100644
--- a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
+++ b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
@@ -1,10 +1,11 @@
+import ApiTask from "@/feature/common/data/api-task"
 import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param"
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status"
 
 export default interface InvoiceRepo {
     fetchAllInvoicesAmount(): Promise<number>
     fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary>
-    createInvoice(params: InvoiceParam): Promise<string>
+    createInvoice(params: InvoiceParam): ApiTask<string>
 }
 
 export const invoiceRepoKey = "invoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
index 73a0106..9cfc63d 100644
--- a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
+++ b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
@@ -1,19 +1,24 @@
 "use server"
+import { ApiEither } from "@/feature/common/data/api-task";
+import ParamsFailure from "@/feature/common/failures/params-failure";
 import serverDi from "@/feature/common/server-di";
 import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { InvoiceParam, invoiceSchema } from "@/feature/core/invoice/domain/param/invoice-param";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
+import { pipe } from "fp-ts/lib/function";
+import { chain, fromNullable, left, map, right } from "fp-ts/lib/TaskEither";
 
-export default async function createInvoiceUsecase(params: InvoiceParam): Promise<string | {errorMessage: string}> {
-    const isParamsValid = invoiceSchema.safeParse(params)
-
-    if (!isParamsValid) {
-        return {
-            errorMessage: "Please pass correct params"
-        }
-    }
+export default async function createInvoiceUsecase(params: InvoiceParam): Promise<ApiEither<string>> {
     const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
 
-    return repo.createInvoice(params)
-
+    return pipe(
+        fromNullable(new ParamsFailure())(params),
+        map((params) => invoiceSchema.safeParse(params)),
+        chain((params) => {
+            const isParamsValid = invoiceSchema.safeParse(params)
+            if (!isParamsValid.success) left(new ParamsFailure())
+            return right(params.data as InvoiceParam)
+        }),
+        chain((params) => repo.createInvoice(params))
+    )()
 }
\ No newline at end of file
-- 
2.39.5


From e9029eff76d62fcb12e81f53fc524784c6348266 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Sat, 2 Nov 2024 16:53:22 +0300
Subject: [PATCH 31/37] Add base lang for server

---
 package.json                                  |  3 ++
 .../create-random-invoice.tsx                 |  2 +-
 .../client/nav-links/nav-link-controller.ts   |  0
 .../components/client/nav-links/nav-links.tsx |  2 +-
 .../components/server/card/card-controller.ts |  0
 .../dashboard/components/server/card/card.tsx |  2 +-
 .../server/cards/cards-controller.ts          |  0
 .../components/server/cards/cards.tsx         |  4 +--
 .../latest-invoices-controller.ts             |  0
 .../latest-invoices/latest-invoices.tsx       |  4 +--
 .../revenue-chart/revenue-chart-controller.ts |  0
 .../server/revenue-chart/revenue-chart.tsx    |  2 +-
 .../dashboard/components/server/sidenav.tsx   |  2 +-
 .../components/server/skeletons/skeletons.tsx |  0
 src/app/{ => [lang]}/dashboard/layout.tsx     |  4 +--
 src/app/[lang]/dashboard/loading.tsx          |  5 +++
 .../dashboard/module/dashboard-app-module.ts  |  2 +-
 src/app/{ => [lang]}/dashboard/page.tsx       | 16 +++++----
 .../vm/create-random-invoice-button-vm.ts     |  0
 src/app/{ => [lang]}/page.tsx                 |  0
 src/app/dashboard/loading.tsx                 |  5 ---
 src/bootstrap/i18n/dictionaries/en.ts         | 15 ++++++++
 src/bootstrap/i18n/dictionaries/lang-key.ts   | 13 +++++++
 src/bootstrap/i18n/dictionaries/ru.ts         | 15 ++++++++
 src/bootstrap/i18n/i18n.ts                    | 21 +++++++++++
 src/bootstrap/i18n/settings.ts                | 15 ++++++++
 src/middleware.ts                             | 23 ++++++++++++
 tsconfig.json                                 |  2 +-
 yarn.lock                                     | 36 ++++++++++++++++++-
 29 files changed, 167 insertions(+), 26 deletions(-)
 rename src/app/{ => [lang]}/dashboard/components/client/create-random-invoice/create-random-invoice.tsx (76%)
 rename src/app/{ => [lang]}/dashboard/components/client/nav-links/nav-link-controller.ts (100%)
 rename src/app/{ => [lang]}/dashboard/components/client/nav-links/nav-links.tsx (88%)
 rename src/app/{ => [lang]}/dashboard/components/server/card/card-controller.ts (100%)
 rename src/app/{ => [lang]}/dashboard/components/server/card/card.tsx (86%)
 rename src/app/{ => [lang]}/dashboard/components/server/cards/cards-controller.ts (100%)
 rename src/app/{ => [lang]}/dashboard/components/server/cards/cards.tsx (75%)
 rename src/app/{ => [lang]}/dashboard/components/server/latest-invoices/latest-invoices-controller.ts (100%)
 rename src/app/{ => [lang]}/dashboard/components/server/latest-invoices/latest-invoices.tsx (89%)
 rename src/app/{ => [lang]}/dashboard/components/server/revenue-chart/revenue-chart-controller.ts (100%)
 rename src/app/{ => [lang]}/dashboard/components/server/revenue-chart/revenue-chart.tsx (93%)
 rename src/app/{ => [lang]}/dashboard/components/server/sidenav.tsx (87%)
 rename src/app/{ => [lang]}/dashboard/components/server/skeletons/skeletons.tsx (100%)
 rename src/app/{ => [lang]}/dashboard/layout.tsx (78%)
 create mode 100644 src/app/[lang]/dashboard/loading.tsx
 rename src/app/{ => [lang]}/dashboard/module/dashboard-app-module.ts (81%)
 rename src/app/{ => [lang]}/dashboard/page.tsx (50%)
 rename src/app/{ => [lang]}/dashboard/vm/create-random-invoice-button-vm.ts (100%)
 rename src/app/{ => [lang]}/page.tsx (100%)
 delete mode 100644 src/app/dashboard/loading.tsx
 create mode 100644 src/bootstrap/i18n/dictionaries/en.ts
 create mode 100644 src/bootstrap/i18n/dictionaries/lang-key.ts
 create mode 100644 src/bootstrap/i18n/dictionaries/ru.ts
 create mode 100644 src/bootstrap/i18n/i18n.ts
 create mode 100644 src/bootstrap/i18n/settings.ts
 create mode 100644 src/middleware.ts

diff --git a/package.json b/package.json
index 6fd58e3..8fee4df 100644
--- a/package.json
+++ b/package.json
@@ -17,11 +17,14 @@
     "class-variance-authority": "^0.7.0",
     "clsx": "^2.1.1",
     "fp-ts": "^2.16.9",
+    "i18next": "^23.16.4",
+    "i18next-resources-to-backend": "^1.2.1",
     "lucide-react": "^0.454.0",
     "next": "15.0.2",
     "postgres": "^3.4.5",
     "react": "19.0.0-rc-69d4b800-20241021",
     "react-dom": "19.0.0-rc-69d4b800-20241021",
+    "react-i18next": "^15.1.0",
     "reflect-metadata": "^0.2.2",
     "tailwind-merge": "^2.5.4",
     "tailwindcss-animate": "^1.0.7",
diff --git a/src/app/dashboard/components/client/create-random-invoice/create-random-invoice.tsx b/src/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
similarity index 76%
rename from src/app/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
rename to src/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
index 8a80645..f3f95a4 100644
--- a/src/app/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
+++ b/src/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
@@ -1,7 +1,7 @@
 "use client"
 
 import Button from "@/app/components/button/button"
-import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm"
+import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm"
 import { useDI } from "@/bootstrap/di/di-context"
 import { useRef } from "react"
 
diff --git a/src/app/dashboard/components/client/nav-links/nav-link-controller.ts b/src/app/[lang]/dashboard/components/client/nav-links/nav-link-controller.ts
similarity index 100%
rename from src/app/dashboard/components/client/nav-links/nav-link-controller.ts
rename to src/app/[lang]/dashboard/components/client/nav-links/nav-link-controller.ts
diff --git a/src/app/dashboard/components/client/nav-links/nav-links.tsx b/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
similarity index 88%
rename from src/app/dashboard/components/client/nav-links/nav-links.tsx
rename to src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
index 1aa6072..6aa2ce8 100644
--- a/src/app/dashboard/components/client/nav-links/nav-links.tsx
+++ b/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
@@ -1,5 +1,5 @@
 'use client'
-import navLinkPersonalVM from '@/app/dashboard/components/client/nav-links/nav-link-controller';
+import navLinkPersonalVM from '@/app/[lang]/dashboard/components/client/nav-links/nav-link-controller';
 import clsx from 'clsx';
 import Link from 'next/link'
 
diff --git a/src/app/dashboard/components/server/card/card-controller.ts b/src/app/[lang]/dashboard/components/server/card/card-controller.ts
similarity index 100%
rename from src/app/dashboard/components/server/card/card-controller.ts
rename to src/app/[lang]/dashboard/components/server/card/card-controller.ts
diff --git a/src/app/dashboard/components/server/card/card.tsx b/src/app/[lang]/dashboard/components/server/card/card.tsx
similarity index 86%
rename from src/app/dashboard/components/server/card/card.tsx
rename to src/app/[lang]/dashboard/components/server/card/card.tsx
index 1b22154..303eaec 100644
--- a/src/app/dashboard/components/server/card/card.tsx
+++ b/src/app/[lang]/dashboard/components/server/card/card.tsx
@@ -1,4 +1,4 @@
-import cardController from "@/app/dashboard/components/server/card/card-controller";
+import cardController from "@/app/[lang]/dashboard/components/server/card/card-controller";
 
 
 
diff --git a/src/app/dashboard/components/server/cards/cards-controller.ts b/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts
similarity index 100%
rename from src/app/dashboard/components/server/cards/cards-controller.ts
rename to src/app/[lang]/dashboard/components/server/cards/cards-controller.ts
diff --git a/src/app/dashboard/components/server/cards/cards.tsx b/src/app/[lang]/dashboard/components/server/cards/cards.tsx
similarity index 75%
rename from src/app/dashboard/components/server/cards/cards.tsx
rename to src/app/[lang]/dashboard/components/server/cards/cards.tsx
index e8f74a9..a0aea58 100644
--- a/src/app/dashboard/components/server/cards/cards.tsx
+++ b/src/app/[lang]/dashboard/components/server/cards/cards.tsx
@@ -1,5 +1,5 @@
-import { Card } from '@/app/dashboard/components/server/card/card';
-import cardsController from '@/app/dashboard/components/server/cards/cards-controller';
+import { Card } from '@/app/[lang]/dashboard/components/server/card/card';
+import cardsController from '@/app/[lang]/dashboard/components/server/cards/cards-controller';
 
 
 export default async function CardWrapper() {
diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
similarity index 100%
rename from src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
rename to src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices.tsx
similarity index 89%
rename from src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
rename to src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices.tsx
index 696af73..3c3e687 100644
--- a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx
+++ b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices.tsx
@@ -1,5 +1,5 @@
-import CreateRandomInvoiceContainer from '@/app/dashboard/components/client/create-random-invoice/create-random-invoice';
-import latestInvoicesController from '@/app/dashboard/components/server/latest-invoices/latest-invoices-controller';
+import CreateRandomInvoiceContainer from '@/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice';
+import latestInvoicesController from '@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller';
 import { ArrowPathIcon } from '@heroicons/react/24/outline';
 import clsx from 'clsx';
 import { isLeft } from 'fp-ts/lib/Either';
diff --git a/src/app/dashboard/components/server/revenue-chart/revenue-chart-controller.ts b/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller.ts
similarity index 100%
rename from src/app/dashboard/components/server/revenue-chart/revenue-chart-controller.ts
rename to src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller.ts
diff --git a/src/app/dashboard/components/server/revenue-chart/revenue-chart.tsx b/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart.tsx
similarity index 93%
rename from src/app/dashboard/components/server/revenue-chart/revenue-chart.tsx
rename to src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart.tsx
index b29a7ae..fb778f2 100644
--- a/src/app/dashboard/components/server/revenue-chart/revenue-chart.tsx
+++ b/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart.tsx
@@ -1,4 +1,4 @@
-import revenueChartController from '@/app/dashboard/components/server/revenue-chart/revenue-chart-controller';
+import revenueChartController from '@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller';
 import { CalendarIcon } from '@heroicons/react/24/outline';
 
 export default async function RevenueChart() {
diff --git a/src/app/dashboard/components/server/sidenav.tsx b/src/app/[lang]/dashboard/components/server/sidenav.tsx
similarity index 87%
rename from src/app/dashboard/components/server/sidenav.tsx
rename to src/app/[lang]/dashboard/components/server/sidenav.tsx
index 0af5a13..bc396c4 100644
--- a/src/app/dashboard/components/server/sidenav.tsx
+++ b/src/app/[lang]/dashboard/components/server/sidenav.tsx
@@ -1,4 +1,4 @@
-import NavLinks from '@/app/dashboard/components/client/nav-links/nav-links';
+import NavLinks from '@/app/[lang]/dashboard/components/client/nav-links/nav-links';
 import Link from 'next/link';
 
 export default function SideNav() {
diff --git a/src/app/dashboard/components/server/skeletons/skeletons.tsx b/src/app/[lang]/dashboard/components/server/skeletons/skeletons.tsx
similarity index 100%
rename from src/app/dashboard/components/server/skeletons/skeletons.tsx
rename to src/app/[lang]/dashboard/components/server/skeletons/skeletons.tsx
diff --git a/src/app/dashboard/layout.tsx b/src/app/[lang]/dashboard/layout.tsx
similarity index 78%
rename from src/app/dashboard/layout.tsx
rename to src/app/[lang]/dashboard/layout.tsx
index 55125e7..4e678cf 100644
--- a/src/app/dashboard/layout.tsx
+++ b/src/app/[lang]/dashboard/layout.tsx
@@ -1,6 +1,6 @@
 "use client"
-import SideNav from "@/app/dashboard/components/server/sidenav";
-import dashboardAppModule from "@/app/dashboard/module/dashboard-app-module";
+import SideNav from "@/app/[lang]/dashboard/components/server/sidenav";
+import dashboardAppModule from "@/app/[lang]/dashboard/module/dashboard-app-module";
 import { DiContext } from "@/bootstrap/di/di-context";
 import { useRef } from "react";
 
diff --git a/src/app/[lang]/dashboard/loading.tsx b/src/app/[lang]/dashboard/loading.tsx
new file mode 100644
index 0000000..9c38c20
--- /dev/null
+++ b/src/app/[lang]/dashboard/loading.tsx
@@ -0,0 +1,5 @@
+import DashboardSkeleton from "@/app/[lang]/dashboard/components/server/skeletons/skeletons";
+
+export default function Loading() {
+  return <DashboardSkeleton />;
+}
\ No newline at end of file
diff --git a/src/app/dashboard/module/dashboard-app-module.ts b/src/app/[lang]/dashboard/module/dashboard-app-module.ts
similarity index 81%
rename from src/app/dashboard/module/dashboard-app-module.ts
rename to src/app/[lang]/dashboard/module/dashboard-app-module.ts
index 60721eb..10992a6 100644
--- a/src/app/dashboard/module/dashboard-app-module.ts
+++ b/src/app/[lang]/dashboard/module/dashboard-app-module.ts
@@ -1,4 +1,4 @@
-import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm";
+import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm";
 import di from "@/bootstrap/di/init-di"
 import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
 
diff --git a/src/app/dashboard/page.tsx b/src/app/[lang]/dashboard/page.tsx
similarity index 50%
rename from src/app/dashboard/page.tsx
rename to src/app/[lang]/dashboard/page.tsx
index 24fb89a..a77f734 100644
--- a/src/app/dashboard/page.tsx
+++ b/src/app/[lang]/dashboard/page.tsx
@@ -1,15 +1,17 @@
-import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/dashboard/components/server/skeletons/skeletons";
-import CardWrapper from "@/app/dashboard/components/server/cards/cards";
-import LatestInvoices from "@/app/dashboard/components/server/latest-invoices/latest-invoices";
-import RevenueChart from "@/app/dashboard/components/server/revenue-chart/revenue-chart";
+import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/[lang]/dashboard/components/server/skeletons/skeletons";
+import CardWrapper from "@/app/[lang]/dashboard/components/server/cards/cards";
+import LatestInvoices from "@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices";
+import RevenueChart from "@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart";
 import { Suspense } from "react";
+import { getTranslation } from "@/bootstrap/i18n/i18n";
 
-export default async function Dashboard() {
-
+export default async function Dashboard(props: {params: Promise<{lang: string}>}) {
+  const {lang} = await props.params 
+  const { t } = await getTranslation(lang)
   return (
     <main>
       <h1 className={`mb-4 text-xl md:text-2xl`}>
-        Dashboard
+        {t("global.dashboard")} 
       </h1>
       <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
         <CardWrapper />
diff --git a/src/app/dashboard/vm/create-random-invoice-button-vm.ts b/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
similarity index 100%
rename from src/app/dashboard/vm/create-random-invoice-button-vm.ts
rename to src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
diff --git a/src/app/page.tsx b/src/app/[lang]/page.tsx
similarity index 100%
rename from src/app/page.tsx
rename to src/app/[lang]/page.tsx
diff --git a/src/app/dashboard/loading.tsx b/src/app/dashboard/loading.tsx
deleted file mode 100644
index c50ad40..0000000
--- a/src/app/dashboard/loading.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import DashboardSkeleton from "@/app/dashboard/components/server/skeletons/skeletons";
-
-export default function Loading() {
-  return <DashboardSkeleton />;
-}
\ No newline at end of file
diff --git a/src/bootstrap/i18n/dictionaries/en.ts b/src/bootstrap/i18n/dictionaries/en.ts
new file mode 100644
index 0000000..3bd88ce
--- /dev/null
+++ b/src/bootstrap/i18n/dictionaries/en.ts
@@ -0,0 +1,15 @@
+import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
+
+const en: typeof langKey = {
+    global: {
+        home: "Home",
+        dashboard: "Dashboard"
+    },
+    dashboard: {
+        invoice: {
+            createButton: "Create random Invoice"
+        }
+    }
+}
+
+export default en
\ No newline at end of file
diff --git a/src/bootstrap/i18n/dictionaries/lang-key.ts b/src/bootstrap/i18n/dictionaries/lang-key.ts
new file mode 100644
index 0000000..aed439c
--- /dev/null
+++ b/src/bootstrap/i18n/dictionaries/lang-key.ts
@@ -0,0 +1,13 @@
+const langKey = {
+    global: {
+        home: "Дом",
+        dashboard: "Панель приборов"
+    },
+    dashboard: {
+        invoice: {
+            createButton: "Создать случайный счет-фактуру"
+        }
+    }
+}
+
+export default langKey;
\ No newline at end of file
diff --git a/src/bootstrap/i18n/dictionaries/ru.ts b/src/bootstrap/i18n/dictionaries/ru.ts
new file mode 100644
index 0000000..5b6c4b4
--- /dev/null
+++ b/src/bootstrap/i18n/dictionaries/ru.ts
@@ -0,0 +1,15 @@
+import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
+
+const ru: typeof langKey = {
+    global: {
+        home: "Дом",
+        dashboard: "Панель приборов"
+    },
+    dashboard: {
+        invoice: {
+            createButton: "Создать случайный счет-фактуру"
+        }
+    }
+}
+
+export default ru
\ No newline at end of file
diff --git a/src/bootstrap/i18n/i18n.ts b/src/bootstrap/i18n/i18n.ts
new file mode 100644
index 0000000..9cc5fb0
--- /dev/null
+++ b/src/bootstrap/i18n/i18n.ts
@@ -0,0 +1,21 @@
+import { getOptions } from '@/bootstrap/i18n/settings'
+import { createInstance } from 'i18next'
+import resourcesToBackend from 'i18next-resources-to-backend'
+import { initReactI18next } from 'react-i18next/initReactI18next'
+
+const initI18next = async (lng: string, ns?: string) => {
+  const i18nInstance = createInstance()
+  await i18nInstance
+    .use(initReactI18next)
+    .use(resourcesToBackend((language: string) => import(`./dictionaries/${language}.ts`)))
+    .init(getOptions(lng, ns))
+  return i18nInstance
+}
+
+export async function getTranslation(lng: string, ns?: string, options: {keyPrefix?: string} = {}) {
+  const i18nextInstance = await initI18next(lng, ns)
+  return {
+    t: i18nextInstance.getFixedT(lng, Array.isArray(ns) ? ns[0] : ns, options?.keyPrefix),
+    i18n: i18nextInstance
+  }
+}
\ No newline at end of file
diff --git a/src/bootstrap/i18n/settings.ts b/src/bootstrap/i18n/settings.ts
new file mode 100644
index 0000000..90f4fa0
--- /dev/null
+++ b/src/bootstrap/i18n/settings.ts
@@ -0,0 +1,15 @@
+export const fallbackLng = 'en'
+export const languages = [fallbackLng, 'ru']
+export const defaultNS = 'translation'
+
+export function getOptions (lng = fallbackLng, ns = defaultNS) {
+  return {
+    // debug: true,
+    supportedLngs: languages,
+    fallbackLng,
+    lng,
+    fallbackNS: defaultNS,
+    defaultNS,
+    ns
+  }
+}
\ No newline at end of file
diff --git a/src/middleware.ts b/src/middleware.ts
new file mode 100644
index 0000000..ee54225
--- /dev/null
+++ b/src/middleware.ts
@@ -0,0 +1,23 @@
+import { fallbackLng, languages } from "@/bootstrap/i18n/settings";
+import { NextRequest, NextResponse } from "next/server";
+ 
+export function middleware(request: NextRequest) {
+  const { pathname } = request.nextUrl
+  const pathnameHasLocale = languages.some(
+    (lang) => pathname.startsWith(`/${lang}/`) || pathname === `/${lang}`
+  )
+ 
+  if (pathnameHasLocale) return
+ 
+  request.nextUrl.pathname = `/${fallbackLng}${pathname}`
+  // e.g. incoming request is /products
+  // The new URL is now /en-US/products
+  return NextResponse.redirect(request.nextUrl)
+}
+ 
+export const config = {
+  matcher: [
+    // Skip all internal paths (_next)
+    '/((?!api|_next/static|_next/image|favicon.ico).*)'
+  ],
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 0d0b9fb..9f3a85a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -25,6 +25,6 @@
       "@/*": ["./src/*"]
     }
   },
-  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/bootstrap/boundaries/db/seed.js", "src/bootstrap/boundaries/db/placeholder-data.js"],
+  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/bootstrap/boundaries/db/seed.js", "src/bootstrap/boundaries/db/placeholder-data.js", "src/middleware.ts"],
   "exclude": ["node_modules"]
 }
diff --git a/yarn.lock b/yarn.lock
index 194f43b..4fc4079 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -138,7 +138,7 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.25.9"
 
-"@babel/runtime@^7.12.5":
+"@babel/runtime@^7.12.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0":
   version "7.26.0"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
   integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
@@ -2342,6 +2342,13 @@ html-encoding-sniffer@^4.0.0:
   dependencies:
     whatwg-encoding "^3.1.1"
 
+html-parse-stringify@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
+  integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
+  dependencies:
+    void-elements "3.1.0"
+
 http-proxy-agent@^7.0.2:
   version "7.0.2"
   resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
@@ -2366,6 +2373,20 @@ https-proxy-agent@^7.0.5:
     agent-base "^7.0.2"
     debug "4"
 
+i18next-resources-to-backend@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz#fded121e63e3139ce839c9901b9449dbbea7351d"
+  integrity sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==
+  dependencies:
+    "@babel/runtime" "^7.23.2"
+
+i18next@^23.16.4:
+  version "23.16.4"
+  resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.16.4.tgz#79c07544f6d6fa803fe8427108d547542c1d6cf4"
+  integrity sha512-9NIYBVy9cs4wIqzurf7nLXPyf3R78xYbxExVqHLK9od3038rjpyOEzW+XB130kZ1N4PZ9inTtJ471CRJ4Ituyg==
+  dependencies:
+    "@babel/runtime" "^7.23.2"
+
 iconv-lite@0.6.3:
   version "0.6.3"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
@@ -3291,6 +3312,14 @@ react-dom@19.0.0-rc-69d4b800-20241021:
   dependencies:
     scheduler "0.25.0-rc-69d4b800-20241021"
 
+react-i18next@^15.1.0:
+  version "15.1.0"
+  resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.1.0.tgz#9494e4add2389f04c205dd7628c1aa75747b98a3"
+  integrity sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q==
+  dependencies:
+    "@babel/runtime" "^7.25.0"
+    html-parse-stringify "^3.0.1"
+
 react-is@^16.13.1:
   version "16.13.1"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -4099,6 +4128,11 @@ vitest@^2.1.4:
     vite-node "2.1.4"
     why-is-node-running "^2.3.0"
 
+void-elements@3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
+  integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
+
 w3c-xmlserializer@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c"
-- 
2.39.5


From 7f51bb6a69c4b717dcb72c09a6a68b78fc5e0efd Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Sat, 2 Nov 2024 20:41:38 +0300
Subject: [PATCH 32/37] Add i18n

---
 package.json                                  |   4 +
 src/app/[lang]/dashboard/page.tsx             |   7 +-
 .../vm/create-random-invoice-button-vm.ts     |   6 +-
 src/app/[lang]/layout.tsx                     |   9 ++
 src/app/layout.tsx                            |   2 +-
 src/bootstrap/helpers/global-helpers.ts       |   2 +
 src/bootstrap/helpers/lib/actions.ts          | 117 ------------------
 src/bootstrap/i18n/dictionaries/en.ts         |   1 +
 src/bootstrap/i18n/dictionaries/lang-key.ts   |   7 +-
 src/bootstrap/i18n/dictionaries/ru.ts         |   1 +
 src/bootstrap/i18n/i18n-provider.tsx          |  13 ++
 src/bootstrap/i18n/i18n.ts                    |  29 +++--
 src/bootstrap/i18n/settings.ts                |   1 +
 src/middleware.ts                             |  55 ++++----
 yarn.lock                                     |  85 ++++++++++++-
 15 files changed, 183 insertions(+), 156 deletions(-)
 create mode 100644 src/app/[lang]/layout.tsx
 delete mode 100644 src/bootstrap/helpers/lib/actions.ts
 create mode 100644 src/bootstrap/i18n/i18n-provider.tsx

diff --git a/package.json b/package.json
index 8fee4df..818e3a6 100644
--- a/package.json
+++ b/package.json
@@ -14,15 +14,19 @@
     "@heroicons/react": "^2.1.5",
     "@radix-ui/react-icons": "^1.3.1",
     "@radix-ui/react-slot": "^1.1.0",
+    "accept-language": "^3.0.20",
     "class-variance-authority": "^0.7.0",
     "clsx": "^2.1.1",
     "fp-ts": "^2.16.9",
     "i18next": "^23.16.4",
+    "i18next-browser-languagedetector": "^8.0.0",
     "i18next-resources-to-backend": "^1.2.1",
     "lucide-react": "^0.454.0",
     "next": "15.0.2",
+    "next-i18n-router": "^5.5.1",
     "postgres": "^3.4.5",
     "react": "19.0.0-rc-69d4b800-20241021",
+    "react-cookie": "^7.2.2",
     "react-dom": "19.0.0-rc-69d4b800-20241021",
     "react-i18next": "^15.1.0",
     "reflect-metadata": "^0.2.2",
diff --git a/src/app/[lang]/dashboard/page.tsx b/src/app/[lang]/dashboard/page.tsx
index a77f734..0035a7b 100644
--- a/src/app/[lang]/dashboard/page.tsx
+++ b/src/app/[lang]/dashboard/page.tsx
@@ -3,15 +3,16 @@ import CardWrapper from "@/app/[lang]/dashboard/components/server/cards/cards";
 import LatestInvoices from "@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices";
 import RevenueChart from "@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart";
 import { Suspense } from "react";
-import { getTranslation } from "@/bootstrap/i18n/i18n";
+import { getServerTranslation } from "@/bootstrap/i18n/i18n";
+import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
 
 export default async function Dashboard(props: {params: Promise<{lang: string}>}) {
   const {lang} = await props.params 
-  const { t } = await getTranslation(lang)
+  const { t } = await getServerTranslation(lang)
   return (
     <main>
       <h1 className={`mb-4 text-xl md:text-2xl`}>
-        {t("global.dashboard")} 
+        {t(langKey.global.dashboard)} 
       </h1>
       <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
         <CardWrapper />
diff --git a/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts b/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
index c5ceb13..136f5b0 100644
--- a/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
+++ b/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
@@ -2,10 +2,12 @@ import ButtonVm from "@/app/components/button/button-vm";
 import { useServerAction } from "@/bootstrap/helpers/hooks/use-server-action";
 import useThrottle from "@/bootstrap/helpers/hooks/use-throttle";
 import BaseVM from "@/bootstrap/helpers/vm/base-vm";
+import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
 import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
 import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
 import { faker } from "@faker-js/faker";
 import { useRouter } from "next/navigation";
+import { useTranslation } from "react-i18next";
 export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
     private createInvoice: typeof createInvoiceUsecase
 
@@ -19,9 +21,11 @@ export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
         const [action, isPending] = useServerAction(() => this.onClickHandler(router.refresh))
         const throttledOnClick = useThrottle(action, 5000)
 
+        const {t} = useTranslation()
+        
         return {
             props: {
-                title: isPending ? "Loading" : "Create Random Invoice",
+                title: t(isPending ? langKey.global.loading : langKey.dashboard.invoice.createButton),
                 isDisable: isPending ? true : false
             },
             onClick: throttledOnClick.bind(this)
diff --git a/src/app/[lang]/layout.tsx b/src/app/[lang]/layout.tsx
new file mode 100644
index 0000000..dedc58e
--- /dev/null
+++ b/src/app/[lang]/layout.tsx
@@ -0,0 +1,9 @@
+import { initI18next } from "@/bootstrap/i18n/i18n";
+import TranslationsProvider from "@/bootstrap/i18n/i18n-provider";
+import { PropsWithChildren } from "react";
+
+export default async function layout(props: PropsWithChildren & {params: Promise<{lang: string}>}) {
+    const lang = (await props.params).lang
+    const { resources} = await initI18next({lng: lang})
+    return <TranslationsProvider lng={lang} resources={resources}>{props.children}</TranslationsProvider>
+}
\ No newline at end of file
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index ba8b4a7..8a8df4e 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -23,7 +23,7 @@ export default function RootLayout({
   children: React.ReactNode;
 }>) {
   return (
-    <html lang="en">
+    <html>
       <body
         className={`${geistSans.variable} ${geistMono.variable} antialiased`}
       >
diff --git a/src/bootstrap/helpers/global-helpers.ts b/src/bootstrap/helpers/global-helpers.ts
index e69de29..cf37914 100644
--- a/src/bootstrap/helpers/global-helpers.ts
+++ b/src/bootstrap/helpers/global-helpers.ts
@@ -0,0 +1,2 @@
+
+export const isServer = typeof window === 'undefined'
\ No newline at end of file
diff --git a/src/bootstrap/helpers/lib/actions.ts b/src/bootstrap/helpers/lib/actions.ts
deleted file mode 100644
index a408241..0000000
--- a/src/bootstrap/helpers/lib/actions.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-'use server';
- 
-import { z } from 'zod';
-import { revalidatePath } from 'next/cache';
-import { redirect } from 'next/navigation';
-import { sql } from '@/bootstrap/boundaries/db/db';
-
-const FormSchema = z.object({
-  id: z.string(),
-  customerId: z.string({
-    invalid_type_error: 'Please select a customer.',
-  }),
-  amount: z.coerce.number().gt(0, { message: 'Please enter an amount greater than $0.' }),
-  status: z.enum(['pending', 'paid'], {
-    invalid_type_error: 'Please select an invoice status.',
-  }),
-  date: z.string(),
-});
-const CreateInvoice = FormSchema.omit({ id: true, date: true });
- 
-
-
-// This is temporary
-export type State = {
-  errors?: {
-    customerId?: string[];
-    amount?: string[];
-    status?: string[];
-  };
-  message?: string | null;
-};
-
-export async function createInvoice(_prevState: State, formData: FormData) {
-  // Validate form fields using Zod
-  console.log('ffor', formData)
-  const validatedFields = CreateInvoice.safeParse({
-    customerId: formData?.get('customerId') || undefined,
-    amount: formData?.get('amount') || undefined,
-    status: formData?.get('status') || undefined,
-  });
-
-  // If form validation fails, return errors early. Otherwise, continue.
-  if (!validatedFields.success) {
-    return {
-      errors: validatedFields.error.flatten().fieldErrors,
-      message: 'Missing Fields. Failed to Create Invoice.',
-    };
-  }
-
-  // Prepare data for insertion into the database
-  const { customerId, amount, status } = validatedFields.data;
-  const amountInCents = amount * 100;
-  const date = new Date().toISOString().split('T')[0];
-
-  // Insert data into the database
-  try {
-    await sql`
-      INSERT INTO invoices (customer_id, amount, status, date)
-      VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
-    `;
-  } catch {
-    // If a database error occurs, return a more specific error.
-    return {
-      message: 'Database Error: Failed to Create Invoice.',
-    };
-  }
-
-  // Revalidate the cache for the invoices page and redirect the user.
-  revalidatePath('/dashboard/invoices');
-  redirect('/dashboard/invoices');
-}
-
-const UpdateInvoice = FormSchema.omit({ id: true, date: true });
-export async function updateInvoice(
-  id: string,
-  _prevState: State,
-  formData: FormData,
-) {
-  const validatedFields = UpdateInvoice.safeParse({
-    customerId: formData.get('customerId'),
-    amount: formData.get('amount'),
-    status: formData.get('status'),
-  });
- 
-  if (!validatedFields.success) {
-    return {
-      errors: validatedFields.error.flatten().fieldErrors,
-      message: 'Missing Fields. Failed to Update Invoice.',
-    };
-  }
- 
-  const { customerId, amount, status } = validatedFields.data;
-  const amountInCents = amount * 100;
- 
-  try {
-    await sql`
-      UPDATE invoices
-      SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status}
-      WHERE id = ${id}
-    `;
-  } catch {
-    return { message: 'Database Error: Failed to Update Invoice.' };
-  }
- 
-  revalidatePath('/dashboard/invoices');
-  redirect('/dashboard/invoices');
-}
-export async function deleteInvoice(id: string) {
-  try {
-    await sql`DELETE FROM invoices WHERE id = ${id}`;
-    revalidatePath('/dashboard/invoices');
-    return { message: 'Deleted Invoice.' };
-  } catch {
-    return { message: 'Database Error: Failed to Delete Invoice.' };
-  } 
-}
-
diff --git a/src/bootstrap/i18n/dictionaries/en.ts b/src/bootstrap/i18n/dictionaries/en.ts
index 3bd88ce..3f0f60c 100644
--- a/src/bootstrap/i18n/dictionaries/en.ts
+++ b/src/bootstrap/i18n/dictionaries/en.ts
@@ -3,6 +3,7 @@ import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
 const en: typeof langKey = {
     global: {
         home: "Home",
+        loading: "Loading",
         dashboard: "Dashboard"
     },
     dashboard: {
diff --git a/src/bootstrap/i18n/dictionaries/lang-key.ts b/src/bootstrap/i18n/dictionaries/lang-key.ts
index aed439c..69ca04e 100644
--- a/src/bootstrap/i18n/dictionaries/lang-key.ts
+++ b/src/bootstrap/i18n/dictionaries/lang-key.ts
@@ -1,11 +1,12 @@
 const langKey = {
     global: {
-        home: "Дом",
-        dashboard: "Панель приборов"
+        home: "global.home",
+        dashboard: "global.dashboard",
+        loading: "global.loading"
     },
     dashboard: {
         invoice: {
-            createButton: "Создать случайный счет-фактуру"
+            createButton: "dashboard.invoice.createButton"
         }
     }
 }
diff --git a/src/bootstrap/i18n/dictionaries/ru.ts b/src/bootstrap/i18n/dictionaries/ru.ts
index 5b6c4b4..9c8e561 100644
--- a/src/bootstrap/i18n/dictionaries/ru.ts
+++ b/src/bootstrap/i18n/dictionaries/ru.ts
@@ -3,6 +3,7 @@ import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
 const ru: typeof langKey = {
     global: {
         home: "Дом",
+        loading: "Загрузка",
         dashboard: "Панель приборов"
     },
     dashboard: {
diff --git a/src/bootstrap/i18n/i18n-provider.tsx b/src/bootstrap/i18n/i18n-provider.tsx
new file mode 100644
index 0000000..d15b347
--- /dev/null
+++ b/src/bootstrap/i18n/i18n-provider.tsx
@@ -0,0 +1,13 @@
+"use client"
+import { I18nextProvider } from "react-i18next"
+import { initI18next } from "@/bootstrap/i18n/i18n";
+import { createInstance, Resource } from "i18next";
+import { PropsWithChildren } from "react";
+
+export default function TranslationsProvider({children, lng, resources}: PropsWithChildren & {lng: string; resources: Resource}) {
+    const i18n = createInstance()
+
+    initI18next({lng, i18n, resources})
+
+    return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
+}
\ No newline at end of file
diff --git a/src/bootstrap/i18n/i18n.ts b/src/bootstrap/i18n/i18n.ts
index 9cc5fb0..edd45d5 100644
--- a/src/bootstrap/i18n/i18n.ts
+++ b/src/bootstrap/i18n/i18n.ts
@@ -1,19 +1,32 @@
-import { getOptions } from '@/bootstrap/i18n/settings'
-import { createInstance } from 'i18next'
+import { getOptions, languages } from '@/bootstrap/i18n/settings'
+import { createInstance, i18n, Resource } from 'i18next'
 import resourcesToBackend from 'i18next-resources-to-backend'
 import { initReactI18next } from 'react-i18next/initReactI18next'
 
-const initI18next = async (lng: string, ns?: string) => {
-  const i18nInstance = createInstance()
+export const initI18next = async (params: {lng: string, i18n?: i18n,  resources?: Resource, ns?: string}) => {
+    const { lng, i18n, ns, resources } = params
+  const i18nInstance = i18n ? i18n : createInstance()
   await i18nInstance
     .use(initReactI18next)
     .use(resourcesToBackend((language: string) => import(`./dictionaries/${language}.ts`)))
-    .init(getOptions(lng, ns))
-  return i18nInstance
+    .init({
+        ...getOptions(lng, ns),
+        resources,
+        preload: resources ? [] : languages 
+    },)
+
+    await i18nInstance.init()
+ 
+    return {
+        i18n: i18nInstance,
+        resources: i18nInstance.services.resourceStore.data,
+        t: i18nInstance.t
+    } 
 }
 
-export async function getTranslation(lng: string, ns?: string, options: {keyPrefix?: string} = {}) {
-  const i18nextInstance = await initI18next(lng, ns)
+export async function getServerTranslation(lng: string, ns?: string, options: {keyPrefix?: string} = {}) {
+  const i18nextInstance = (await initI18next({lng, ns})).i18n
+  
   return {
     t: i18nextInstance.getFixedT(lng, Array.isArray(ns) ? ns[0] : ns, options?.keyPrefix),
     i18n: i18nextInstance
diff --git a/src/bootstrap/i18n/settings.ts b/src/bootstrap/i18n/settings.ts
index 90f4fa0..7e3030b 100644
--- a/src/bootstrap/i18n/settings.ts
+++ b/src/bootstrap/i18n/settings.ts
@@ -1,6 +1,7 @@
 export const fallbackLng = 'en'
 export const languages = [fallbackLng, 'ru']
 export const defaultNS = 'translation'
+export const cookieName = 'i18next'
 
 export function getOptions (lng = fallbackLng, ns = defaultNS) {
   return {
diff --git a/src/middleware.ts b/src/middleware.ts
index ee54225..8c6f760 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -1,23 +1,36 @@
-import { fallbackLng, languages } from "@/bootstrap/i18n/settings";
-import { NextRequest, NextResponse } from "next/server";
- 
-export function middleware(request: NextRequest) {
-  const { pathname } = request.nextUrl
-  const pathnameHasLocale = languages.some(
-    (lang) => pathname.startsWith(`/${lang}/`) || pathname === `/${lang}`
-  )
- 
-  if (pathnameHasLocale) return
- 
-  request.nextUrl.pathname = `/${fallbackLng}${pathname}`
-  // e.g. incoming request is /products
-  // The new URL is now /en-US/products
-  return NextResponse.redirect(request.nextUrl)
-}
- 
+import { NextRequest, NextResponse } from 'next/server'
+import acceptLanguage from 'accept-language'
+import { cookieName, fallbackLng, languages } from '@/bootstrap/i18n/settings'
+
+acceptLanguage.languages(languages)
+
 export const config = {
-  matcher: [
-    // Skip all internal paths (_next)
-    '/((?!api|_next/static|_next/image|favicon.ico).*)'
-  ],
+  matcher: ["/((?!api|static|.*\\..*|_next).*)"]
+}
+
+export function middleware(req: NextRequest) {
+  let lng
+  if (req.cookies.has(cookieName)) lng = acceptLanguage.get(req?.cookies?.get(cookieName)?.value)
+  if (!lng) lng = acceptLanguage.get(req.headers.get('Accept-Language'))
+  if (!lng) lng = fallbackLng
+
+  // Redirect if lng in path is not supported
+  if (
+    !languages.some(loc => req.nextUrl.pathname.startsWith(`/${loc}`)) &&
+    !req.nextUrl.pathname.startsWith('/_next')
+  ) {
+    return NextResponse.redirect(new URL(`/${lng}${req.nextUrl.pathname}`, req.url))
+  }
+
+  if (req.headers.has('referer')) {
+    const refererUrl = new URL(req?.headers?.get('referer') ?? "")
+    const lngInReferer = languages.find((l) => refererUrl.pathname.startsWith(`/${l}`))
+    const response = NextResponse.next()
+    if (lngInReferer) {
+      response.cookies.set(cookieName, lngInReferer)
+    }
+    return response
+  }
+
+  return NextResponse.next()
 }
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 4fc4079..cac9504 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -334,6 +334,13 @@
   resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.1.0.tgz#5d7957df87e2fb0eee5dcfd311ba83b34ec8eead"
   integrity sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug==
 
+"@formatjs/intl-localematcher@^0.5.2":
+  version "0.5.6"
+  resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.6.tgz#cd0cd99483673d3196a15b4e2c924cfda7f002f8"
+  integrity sha512-roz1+Ba5e23AHX6KUAWmLEyTRZegM5YDuxuvkHCyK3RJddf/UXB2f+s7pOMm9ktfPGla0g+mQXOn5vsuYirnaA==
+  dependencies:
+    tslib "2"
+
 "@heroicons/react@^2.1.5":
   version "2.1.5"
   resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.5.tgz#1e13f34976cc542deae92353c01c8b3d7942e9ba"
@@ -801,11 +808,24 @@
   dependencies:
     "@babel/types" "^7.20.7"
 
+"@types/cookie@^0.6.0":
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5"
+  integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==
+
 "@types/estree@1.0.6", "@types/estree@^1.0.0":
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
   integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
 
+"@types/hoist-non-react-statics@^3.3.5":
+  version "3.3.5"
+  resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494"
+  integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==
+  dependencies:
+    "@types/react" "*"
+    hoist-non-react-statics "^3.3.0"
+
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -999,6 +1019,13 @@ abbrev@1:
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
   integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
 
+accept-language@^3.0.20:
+  version "3.0.20"
+  resolved "https://registry.yarnpkg.com/accept-language/-/accept-language-3.0.20.tgz#e825601d3b59f5ac7487698569b6640e80240cda"
+  integrity sha512-xklPzRma4aoDEPk0ZfMjeuxB2FP4JBYlAR25OFUqCoOYDjYo6wGwAs49SnTN/MoB5VpnNX9tENfZ+vEIFmHQMQ==
+  dependencies:
+    bcp47 "^1.1.2"
+
 acorn-jsx@^5.3.2:
   version "5.3.2"
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
@@ -1234,6 +1261,11 @@ balanced-match@^1.0.0:
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
   integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 
+bcp47@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/bcp47/-/bcp47-1.1.2.tgz#354be3307ffd08433a78f5e1e2095845f89fc7fe"
+  integrity sha512-JnkkL4GUpOvvanH9AZPX38CxhiLsXMBicBY2IAtqiVN8YulGDQybUydWA4W6yAMtw6iShtw+8HEF6cfrTHU+UQ==
+
 bcrypt@^5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2"
@@ -1443,6 +1475,11 @@ convert-source-map@^2.0.0:
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
   integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
 
+cookie@^0.7.2:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
+  integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
+
 cross-spawn@^7.0.0, cross-spawn@^7.0.2:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -2335,6 +2372,13 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
   dependencies:
     function-bind "^1.1.2"
 
+hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+  integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+  dependencies:
+    react-is "^16.7.0"
+
 html-encoding-sniffer@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448"
@@ -2373,6 +2417,13 @@ https-proxy-agent@^7.0.5:
     agent-base "^7.0.2"
     debug "4"
 
+i18next-browser-languagedetector@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz#b6fdd9b43af67c47f2c26c9ba27710a1eaf31e2f"
+  integrity sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==
+  dependencies:
+    "@babel/runtime" "^7.23.2"
+
 i18next-resources-to-backend@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz#fded121e63e3139ce839c9901b9449dbbea7351d"
@@ -2956,6 +3007,19 @@ natural-compare@^1.4.0:
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
   integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
 
+negotiator@^0.6.3:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7"
+  integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==
+
+next-i18n-router@^5.5.1:
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/next-i18n-router/-/next-i18n-router-5.5.1.tgz#3d4c34d431e38aa4ba8f52cd54d4387fc70db6d2"
+  integrity sha512-uJGYUAQS33LbRT3Jx+kurR/E79iPQo1jWZUYmc+614UkPt58k2XYyGloSvHR74b21i4K/d6eksdBj6T2WojjdA==
+  dependencies:
+    "@formatjs/intl-localematcher" "^0.5.2"
+    negotiator "^0.6.3"
+
 next@15.0.2:
   version "15.0.2"
   resolved "https://registry.yarnpkg.com/next/-/next-15.0.2.tgz#4a2224c007856118010b8cef5e9b2383cd743388"
@@ -3305,6 +3369,15 @@ queue-microtask@^1.2.2:
   resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
   integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
 
+react-cookie@^7.2.2:
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.2.2.tgz#a7559e552ea9cca39a4b3686723a5acf504b8f84"
+  integrity sha512-e+hi6axHcw9VODoeVu8WyMWyoosa1pzpyjfvrLdF7CexfU+WSGZdDuRfHa4RJgTpfv3ZjdIpHE14HpYBieHFhg==
+  dependencies:
+    "@types/hoist-non-react-statics" "^3.3.5"
+    hoist-non-react-statics "^3.3.2"
+    universal-cookie "^7.0.0"
+
 react-dom@19.0.0-rc-69d4b800-20241021:
   version "19.0.0-rc-69d4b800-20241021"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0-rc-69d4b800-20241021.tgz#e27b4f2c962236e9ece496a0ea1c9c7161608ea0"
@@ -3320,7 +3393,7 @@ react-i18next@^15.1.0:
     "@babel/runtime" "^7.25.0"
     html-parse-stringify "^3.0.1"
 
-react-is@^16.13.1:
+react-is@^16.13.1, react-is@^16.7.0:
   version "16.13.1"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -3968,7 +4041,7 @@ tsconfig-paths@^3.15.0:
     minimist "^1.2.6"
     strip-bom "^3.0.0"
 
-tslib@*, tslib@^2.4.0:
+tslib@*, tslib@2, tslib@^2.4.0:
   version "2.8.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
   integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
@@ -4061,6 +4134,14 @@ undici-types@~6.19.2:
   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
   integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
 
+universal-cookie@^7.0.0:
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.2.2.tgz#93ae9ec55baab89b24300473543170bb8112773c"
+  integrity sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ==
+  dependencies:
+    "@types/cookie" "^0.6.0"
+    cookie "^0.7.2"
+
 update-browserslist-db@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
-- 
2.39.5


From 55c82982370af8f3a1ea44738ec1fca930d50785 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 15 Nov 2024 19:44:26 +0300
Subject: [PATCH 33/37] Fix linting issues

---
 .eslintrc.json                                |  84 ++++++++-
 package.json                                  |   8 +-
 .../create-random-invoice.tsx                 |  18 +-
 .../client/nav-links/nav-link-controller.ts   |  29 ---
 .../client/nav-links/nav-link-vm.ts           |  28 +++
 .../components/client/nav-links/nav-links.tsx |  15 +-
 .../components/server/card/card-controller.ts |  28 +--
 .../dashboard/components/server/card/card.tsx |  12 +-
 .../server/cards/cards-controller.ts          |   4 +-
 .../components/server/cards/cards.tsx         |  18 +-
 .../latest-invoices-controller.ts             |   6 +-
 .../latest-invoices/latest-invoices.tsx       |  90 ++++-----
 .../revenue-chart/revenue-chart-controller.ts |   6 +-
 .../server/revenue-chart/revenue-chart.tsx    |  13 +-
 .../dashboard/components/server/sidenav.tsx   |  10 +-
 .../components/server/skeletons/skeletons.tsx |  47 ++---
 src/app/[lang]/dashboard/layout.tsx           |  12 +-
 src/app/[lang]/dashboard/loading.tsx          |   2 +-
 .../dashboard/module/dashboard-app-module.ts  |  19 +-
 src/app/[lang]/dashboard/page.tsx             |  22 ++-
 .../vm/create-random-invoice-button-vm.ts     |  69 +++----
 src/app/[lang]/layout.tsx                     |  16 +-
 src/app/[lang]/page.tsx                       |  10 +-
 src/app/components/button/button-vm.ts        |  12 +-
 src/app/components/button/button.tsx          |  43 +++--
 src/app/components/icons/document.tsx         |  25 ++-
 src/app/components/icons/home.tsx             |  25 ++-
 src/app/components/icons/user.tsx             |  26 ++-
 src/app/layout.tsx                            |   3 +-
 src/bootstrap/boundaries/db/db.ts             |  13 +-
 .../boundaries/db/placeholder-data.js         | 172 +++++++++---------
 src/bootstrap/boundaries/db/seed.js           |  21 ++-
 src/bootstrap/di/di-context.tsx               |  22 +--
 src/bootstrap/di/init-di.ts                   |   4 +-
 src/bootstrap/helpers/global-helpers.ts       |   3 +-
 .../helpers/hooks/use-server-action.ts        |   8 +-
 src/bootstrap/helpers/hooks/use-throttle.ts   |  28 +--
 src/bootstrap/helpers/lib/ui-utils.ts         |   6 +-
 src/bootstrap/helpers/type-helper.ts          |   2 +-
 src/bootstrap/helpers/view/base-view.tsx      |  17 +-
 src/bootstrap/helpers/vm/base-vm.ts           |   6 +-
 src/bootstrap/i18n/dictionaries/en.ts         |  24 +--
 src/bootstrap/i18n/dictionaries/lang-key.ts   |  22 +--
 src/bootstrap/i18n/dictionaries/ru.ts         |  24 +--
 src/bootstrap/i18n/i18n-provider.tsx          |  19 +-
 src/bootstrap/i18n/i18n.ts                    |  71 +++++---
 src/bootstrap/i18n/settings.ts                |  16 +-
 src/feature/common/data/api-task.ts           |   7 +-
 src/feature/common/failures/base-failure.ts   |   4 +-
 .../common/failures/dev/arguments-failure.ts  |   4 +-
 .../common/failures/dev/base-dev-failure.ts   |   4 +-
 .../common/failures/dev/dependency-failure.ts |   4 +-
 src/feature/common/failures/params-failure.ts |   2 +-
 src/feature/common/feature-helpers.ts         |   9 +-
 src/feature/common/server-di.ts               |  30 +--
 .../data/module/customer-invoice-di.ts        |   8 +-
 .../data/repo/customer-invoice-db-repo.ts     |  59 +++---
 .../domain/entity/customer-invoice.ts         |  42 +++--
 .../domain/i-repo/customer-invoice-repo.ts    |   8 +-
 .../fetch-customer-invoices-usecase.ts        |  18 +-
 .../customer-invoice/invoice-module-key.ts    |   2 +-
 src/feature/core/customer/customer-key.ts     |   2 +-
 .../core/customer/data/module/customer-di.ts  |   8 +-
 .../customer/data/repo/customer-db-repo.ts    | 104 ++++++-----
 .../core/customer/domain/entity/customer.ts   |  56 +++---
 .../customer/domain/i-repo/customer-repo.ts   |  10 +-
 .../usecase/fetch-customers-amount-usecase.ts |  10 +-
 .../domain/usecase/fetch-customers-usecase.ts |  19 +-
 .../core/invoice/data/module/invoice-di.ts    |   8 +-
 .../core/invoice/data/repo/invoice-db-repo.ts |  85 ++++-----
 .../invoice/domain/i-repo/invoice-repo.ts     |  14 +-
 .../invoice/domain/param/invoice-param.ts     |  10 +-
 .../domain/usecase/create-invoice-usecase.ts  |  40 ++--
 .../fetch-all-invoices-amount-usecase.ts      |  13 +-
 .../usecase/fetch-invoices-status-summary.ts  |  10 +-
 .../domain/value-object/invoice-status.ts     |  18 +-
 .../core/invoice/invoice-module-key.ts        |   2 +-
 .../core/revenue/data/module/revenue-di.ts    |  14 +-
 .../core/revenue/data/repo/revenue-db-repo.ts |  48 +++--
 .../core/revenue/domain/entity/revenue.ts     |  20 +-
 .../revenue/domain/i-repo/revenue-repo.ts     |   4 +-
 .../core/revenue/domain/revenue-module-key.ts |   2 +-
 .../domain/usecase/fetch-revenues-usecase.ts  |  10 +-
 .../data/module/summary-info-di.ts            |  32 ++--
 .../domain/summary-info-module-key.ts         |   2 +-
 .../usecase/fetch-summary-info-usecase.ts     |  49 ++---
 .../domain/value-object/summary-info.ts       |  28 +--
 src/middleware.ts                             |  47 ++---
 .../customer/customer-fake-factory.ts         |  33 ++--
 src/test/common/mock/mock-di.ts               |  10 +-
 src/test/setup.ts                             |   2 +-
 .../usecase/fetch-customers-usecase.test.ts   |  55 +++---
 yarn.lock                                     |  82 ++++++++-
 93 files changed, 1268 insertions(+), 958 deletions(-)
 delete mode 100644 src/app/[lang]/dashboard/components/client/nav-links/nav-link-controller.ts
 create mode 100644 src/app/[lang]/dashboard/components/client/nav-links/nav-link-vm.ts

diff --git a/.eslintrc.json b/.eslintrc.json
index 60d067f..6307415 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -9,5 +9,87 @@
       }
     }
   ],
-  "extends": ["next/core-web-vitals", "next/typescript"]
+  "plugins": [
+    "prettier"
+  ],
+  "settings": {
+    "react": {
+      "version": "detect"
+    },
+    "import/resolver": {
+      "alias": {
+        "map": [
+          [
+            "~",
+            "./src"
+          ]
+        ],
+        "extensions": [
+          ".js",
+          ".ts",
+          ".tsx",
+          ".d.ts",
+          ".test.ts",
+          ".json"
+        ]
+      }
+    }
+  },
+  "rules": {
+    "no-use-before-define": "off",
+    "class-methods-use-this": "off",
+    "import/prefer-default-export": "off",
+    "import/no-cycle": "off",
+    "no-promise-executor-return": "off",
+    "@typescript-eslint/no-shadow": "off",
+    "react/require-default-props": "off",
+    "no-shadow": "off",
+    "prettier/prettier": [
+      "warn",
+      {
+        "printWidth": 80,
+        "tabWidth": 2,
+        "endOfLine":"auto",
+        "useTabs": false,
+        "semi": true,
+        "singleQuote": false,
+        "quoteProps": "as-needed",
+        "jsxSingleQuote": false,
+        "trailingComma": "all",
+        "bracketSpacing": true,
+        "arrowParens": "always"
+      }
+    ],
+    "import/extensions": [
+      "error",
+      "ignorePackages",
+      {
+        "js": "never",
+        "jsx": "never",
+        "ts": "never",
+        "tsx": "never"
+      }
+    ],
+    "react/jsx-filename-extension": [
+      1,
+      {
+        "extensions": [
+          ".ts",
+          ".tsx"
+        ]
+      }
+    ],
+    "import/no-extraneous-dependencies": [
+      "error",
+      {
+        "devDependencies": true
+      }
+    ]
+  },
+  "extends": [
+    "airbnb",
+    "next/core-web-vitals", 
+    "next/typescript",
+    "prettier"
+  ]
 }
diff --git a/package.json b/package.json
index 818e3a6..3223cf1 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
     "dev": "next dev --turbopack",
     "build": "next build",
     "start": "next start --port 4000",
-    "lint": "next lint",
+    "lint": "next lint --fix",
     "test": "vitest",
     "seed": "node -r dotenv/config ./src/bootstrap/boundaries/db/seed.js"
   },
@@ -46,10 +46,16 @@
     "bcrypt": "^5.1.1",
     "dotenv": "^16.4.5",
     "eslint": "^8",
+    "eslint-config-airbnb": "^19.0.4",
     "eslint-config-next": "15.0.1",
+    "eslint-config-prettier": "^9.1.0",
+    "eslint-import-resolver-alias": "^1.1.2",
+    "eslint-import-resolver-typescript": "^3.6.3",
+    "eslint-plugin-prettier": "^5.2.1",
     "jsdom": "^25.0.1",
     "moq.ts": "^10.0.8",
     "postcss": "^8",
+    "prettier": "^3.3.3",
     "tailwindcss": "^3.4.1",
     "typescript": "^5",
     "vitest": "^2.1.4"
diff --git a/src/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice.tsx b/src/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
index f3f95a4..68742c2 100644
--- a/src/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
+++ b/src/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice.tsx
@@ -1,13 +1,13 @@
-"use client"
+"use client";
 
-import Button from "@/app/components/button/button"
-import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm"
-import { useDI } from "@/bootstrap/di/di-context"
-import { useRef } from "react"
+import Button from "@/app/components/button/button";
+import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm";
+import { useDI } from "@/bootstrap/di/di-context";
+import { useRef } from "react";
 
 export default function CreateRandomInvoiceContainer() {
-    const di = useDI()
-    const vm = useRef(di.resolve(CreateRandomInvoiceButtonVM))
+  const di = useDI();
+  const vm = useRef(di.resolve(CreateRandomInvoiceButtonVM));
 
-    return <Button vm={vm.current}/>
-}
\ No newline at end of file
+  return <Button vm={vm.current} />;
+}
diff --git a/src/app/[lang]/dashboard/components/client/nav-links/nav-link-controller.ts b/src/app/[lang]/dashboard/components/client/nav-links/nav-link-controller.ts
deleted file mode 100644
index b6a3aa9..0000000
--- a/src/app/[lang]/dashboard/components/client/nav-links/nav-link-controller.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-
-import { DocumentIcon } from '@/app/components/icons/document';
-import HomeIcon from '@/app/components/icons/home';
-import { UserIcon } from '@/app/components/icons/user';
-import { usePathname } from 'next/navigation';
-
-type LinkItem = {
-    name: string; 
-    href: string;
-    icon: (props: {className?: string}) => JSX.Element
-}
-export default function navLinkPersonalVM() {
-    const pathname = usePathname()
-    // Map of links to display in the side navigation.
-    // Depending on the size of the application, this would be stored in a database.
-    const links: LinkItem[] = [
-        { name: 'Home', href: '/dashboard', icon: HomeIcon },
-        {
-            name: 'Invoices',
-            href: '/dashboard/invoices',
-            icon: DocumentIcon,
-        },
-        { name: 'Customers', href: '/dashboard/customers', icon: UserIcon },
-    ];
-    return {
-        links,
-        isLinkActive: (link: LinkItem) => pathname === link.href
-    }
-}
\ No newline at end of file
diff --git a/src/app/[lang]/dashboard/components/client/nav-links/nav-link-vm.ts b/src/app/[lang]/dashboard/components/client/nav-links/nav-link-vm.ts
new file mode 100644
index 0000000..bdf58b2
--- /dev/null
+++ b/src/app/[lang]/dashboard/components/client/nav-links/nav-link-vm.ts
@@ -0,0 +1,28 @@
+import { DocumentIcon } from "@/app/components/icons/document";
+import HomeIcon from "@/app/components/icons/home";
+import { UserIcon } from "@/app/components/icons/user";
+import { usePathname } from "next/navigation";
+
+type LinkItem = {
+  name: string;
+  href: string;
+  icon: (props: { className?: string }) => JSX.Element;
+};
+export default function navLinkPersonalVM() {
+  const pathname = usePathname();
+  // Map of links to display in the side navigation.
+  // Depending on the size of the application, this would be stored in a database.
+  const links: LinkItem[] = [
+    { name: "Home", href: "/dashboard", icon: HomeIcon },
+    {
+      name: "Invoices",
+      href: "/dashboard/invoices",
+      icon: DocumentIcon,
+    },
+    { name: "Customers", href: "/dashboard/customers", icon: UserIcon },
+  ];
+  return {
+    links,
+    isLinkActive: (link: LinkItem) => pathname === link.href,
+  };
+}
diff --git a/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx b/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
index 6aa2ce8..edb724c 100644
--- a/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
+++ b/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
@@ -1,10 +1,11 @@
-'use client'
-import navLinkPersonalVM from '@/app/[lang]/dashboard/components/client/nav-links/nav-link-controller';
-import clsx from 'clsx';
-import Link from 'next/link'
+"use client";
+
+import navLinkPersonalVM from "@/app/[lang]/dashboard/components/client/nav-links/nav-link-vm";
+import clsx from "clsx";
+import Link from "next/link";
 
 export default function NavLinks() {
-  const { links, isLinkActive  } = navLinkPersonalVM()
+  const { links, isLinkActive } = navLinkPersonalVM();
   return (
     <>
       {links.map((link) => {
@@ -14,9 +15,9 @@ export default function NavLinks() {
             key={link.name}
             href={link.href}
             className={clsx(
-              'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3',
+              "flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3",
               {
-                'bg-sky-100 text-blue-600': isLinkActive(link),
+                "bg-sky-100 text-blue-600": isLinkActive(link),
               },
             )}
           >
diff --git a/src/app/[lang]/dashboard/components/server/card/card-controller.ts b/src/app/[lang]/dashboard/components/server/card/card-controller.ts
index 43c40f7..5f28e88 100644
--- a/src/app/[lang]/dashboard/components/server/card/card-controller.ts
+++ b/src/app/[lang]/dashboard/components/server/card/card-controller.ts
@@ -3,17 +3,19 @@ import {
   ClockIcon,
   UserGroupIcon,
   InboxIcon,
-} from '@heroicons/react/24/outline';
+} from "@heroicons/react/24/outline";
 
-export default function cardController(props: {  type: 'invoices' | 'customers' | 'pending' | 'collected'; }) {
-    const { type } = props
-    const iconMap = {
-        collected: BanknotesIcon,
-        customers: UserGroupIcon,
-        pending: ClockIcon,
-        invoices: InboxIcon,
-    };
-    return {
-        Icon: iconMap[type]
-    }
-}
\ No newline at end of file
+export default function cardController(props: {
+  type: "invoices" | "customers" | "pending" | "collected";
+}) {
+  const { type } = props;
+  const iconMap = {
+    collected: BanknotesIcon,
+    customers: UserGroupIcon,
+    pending: ClockIcon,
+    invoices: InboxIcon,
+  };
+  return {
+    Icon: iconMap[type],
+  };
+}
diff --git a/src/app/[lang]/dashboard/components/server/card/card.tsx b/src/app/[lang]/dashboard/components/server/card/card.tsx
index 303eaec..0bb192a 100644
--- a/src/app/[lang]/dashboard/components/server/card/card.tsx
+++ b/src/app/[lang]/dashboard/components/server/card/card.tsx
@@ -1,7 +1,5 @@
 import cardController from "@/app/[lang]/dashboard/components/server/card/card-controller";
 
-
-
 export function Card({
   title,
   value,
@@ -9,9 +7,9 @@ export function Card({
 }: {
   title: string;
   value: number | string;
-  type: 'invoices' | 'customers' | 'pending' | 'collected';
+  type: "invoices" | "customers" | "pending" | "collected";
 }) {
- const { Icon } = cardController({type})
+  const { Icon } = cardController({ type });
 
   return (
     <div className="rounded-xl bg-gray-50 p-2 shadow-sm">
@@ -19,11 +17,9 @@ export function Card({
         {Icon ? <Icon className="h-5 w-5 text-gray-700" /> : null}
         <h3 className="ml-2 text-sm font-medium">{title}</h3>
       </div>
-      <p
-        className="rounded-xl bg-white px-4 py-8 text-center text-2xl"
-      >
+      <p className="rounded-xl bg-white px-4 py-8 text-center text-2xl">
         {value}
       </p>
     </div>
   );
-}
\ No newline at end of file
+}
diff --git a/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts b/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts
index b8aaf19..53c601a 100644
--- a/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts
+++ b/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts
@@ -1,5 +1,5 @@
 import fetchSummaryInfoUsecase from "@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase";
 
 export default function cardsController() {
-   return fetchSummaryInfoUsecase();
-}
\ No newline at end of file
+  return fetchSummaryInfoUsecase();
+}
diff --git a/src/app/[lang]/dashboard/components/server/cards/cards.tsx b/src/app/[lang]/dashboard/components/server/cards/cards.tsx
index a0aea58..91c641d 100644
--- a/src/app/[lang]/dashboard/components/server/cards/cards.tsx
+++ b/src/app/[lang]/dashboard/components/server/cards/cards.tsx
@@ -1,20 +1,16 @@
-import { Card } from '@/app/[lang]/dashboard/components/server/card/card';
-import cardsController from '@/app/[lang]/dashboard/components/server/cards/cards-controller';
-
+import { Card } from "@/app/[lang]/dashboard/components/server/card/card";
+import cardsController from "@/app/[lang]/dashboard/components/server/cards/cards-controller";
 
 export default async function CardWrapper() {
-  const {customersNumber, invoicesNumber, invoicesSummary } = await cardsController();
-  
+  const { customersNumber, invoicesNumber, invoicesSummary } =
+    await cardsController();
+
   return (
     <>
       <Card title="Collected" value={invoicesSummary.paid} type="collected" />
       <Card title="Pending" value={invoicesSummary.pending} type="pending" />
       <Card title="Total Invoices" value={invoicesNumber} type="invoices" />
-      <Card
-        title="Total Customers"
-        value={customersNumber}
-        type="customers"
-      />
+      <Card title="Total Customers" value={customersNumber} type="customers" />
     </>
   );
-}
\ No newline at end of file
+}
diff --git a/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
index 8fbbde9..9f34a9a 100644
--- a/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
+++ b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
@@ -1,5 +1,5 @@
 import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
 
-export default async function latestInvoicesController() {
-    return await fetchCustomerInvoicesUsecase()
-}
\ No newline at end of file
+export default function latestInvoicesController() {
+  return fetchCustomerInvoicesUsecase();
+}
diff --git a/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices.tsx b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices.tsx
index 3c3e687..e55a6e1 100644
--- a/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices.tsx
+++ b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices.tsx
@@ -1,61 +1,49 @@
-import CreateRandomInvoiceContainer from '@/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice';
-import latestInvoicesController from '@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller';
-import { ArrowPathIcon } from '@heroicons/react/24/outline';
-import clsx from 'clsx';
-import { isLeft } from 'fp-ts/lib/Either';
-import Image from 'next/image';
+import CreateRandomInvoiceContainer from "@/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice";
+import latestInvoicesController from "@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller";
+import { ArrowPathIcon } from "@heroicons/react/24/outline";
+import clsx from "clsx";
+import { isLeft } from "fp-ts/lib/Either";
+import Image from "next/image";
 
 export default async function LatestInvoices() {
   const latestInvoices = await latestInvoicesController();
 
-  if (isLeft(latestInvoices)) return <div>Error</div>
-  
-  const invoices = latestInvoices.right.map((invoice, i) => {
-            return (
-              <div
-                key={invoice.id}
-                className={clsx(
-                  'flex flex-row items-center justify-between py-4',
-                  {
-                    'border-t': i !== 0,
-                  },
-                )}
-              >
-                <div className="flex items-center">
-                  <Image
-                    src={invoice.customerImageUrl}
-                    alt={`${invoice.customerName}'s profile picture`}
-                    className="mr-4 rounded-full"
-                    width={32}
-                    height={32}
-                  />
-                  <div className="min-w-0">
-                    <p className="truncate text-sm font-semibold md:text-base">
-                      {invoice.customerName}
-                    </p>
-                    <p className="hidden text-sm text-gray-500 sm:block">
-                      {invoice.customerEmail}
-                    </p>
-                  </div>
-                </div>
-                <p
-                  className="truncate text-sm font-medium md:text-base"
-                >
-                  {invoice.invoicesAmount}
-                </p>
-              </div>
-            );
-          })
+  if (isLeft(latestInvoices)) return <div>Error</div>;
+
+  const invoices = latestInvoices.right.map((invoice, i) => (
+    <div
+      key={invoice.id}
+      className={clsx("flex flex-row items-center justify-between py-4", {
+        "border-t": i !== 0,
+      })}
+    >
+      <div className="flex items-center">
+        <Image
+          src={invoice.customerImageUrl}
+          alt={`${invoice.customerName}'s profile picture`}
+          className="mr-4 rounded-full"
+          width={32}
+          height={32}
+        />
+        <div className="min-w-0">
+          <p className="truncate text-sm font-semibold md:text-base">
+            {invoice.customerName}
+          </p>
+          <p className="hidden text-sm text-gray-500 sm:block">
+            {invoice.customerEmail}
+          </p>
+        </div>
+      </div>
+      <p className="truncate text-sm font-medium md:text-base">
+        {invoice.invoicesAmount}
+      </p>
+    </div>
+  ));
   return (
     <div className="flex w-full flex-col md:col-span-4">
-      <h2 className="mb-4 text-xl md:text-2xl">
-        Latest Invoices
-      </h2>
+      <h2 className="mb-4 text-xl md:text-2xl">Latest Invoices</h2>
       <div className="flex grow flex-col max-h-[66.5vh] justify-between rounded-xl bg-gray-50 p-4">
-
-        <div className="bg-white px-6 h-full overflow-y-auto">
-          {invoices}
-        </div>
+        <div className="bg-white px-6 h-full overflow-y-auto">{invoices}</div>
         <div className="flex items-end mt-auto pb-2 pt-6">
           <ArrowPathIcon className="h-5 w-5 text-gray-500" />
           <h3 className="ml-2 text-sm text-gray-500 ">Updated just now</h3>
diff --git a/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller.ts b/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller.ts
index becc678..55c0797 100644
--- a/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller.ts
+++ b/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller.ts
@@ -11,8 +11,8 @@ export default async function revenueChartController() {
     revenue,
     chartHeight,
     yAxisLabels,
-    topLabel
-  }
+    topLabel,
+  };
 }
 
 function generateYAxis(revenue: Revenue[]) {
@@ -27,4 +27,4 @@ function generateYAxis(revenue: Revenue[]) {
   }
 
   return { yAxisLabels, topLabel };
-};
+}
diff --git a/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart.tsx b/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart.tsx
index fb778f2..e6bc5a0 100644
--- a/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart.tsx
+++ b/src/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart.tsx
@@ -1,8 +1,9 @@
-import revenueChartController from '@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller';
-import { CalendarIcon } from '@heroicons/react/24/outline';
+import revenueChartController from "@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller";
+import { CalendarIcon } from "@heroicons/react/24/outline";
 
 export default async function RevenueChart() {
-  const { chartHeight, revenue, topLabel, yAxisLabels } = await revenueChartController()
+  const { chartHeight, revenue, topLabel, yAxisLabels } =
+    await revenueChartController();
 
   if (!revenue || revenue.length === 0) {
     return <p className="mt-4 text-gray-400">No data available.</p>;
@@ -10,9 +11,7 @@ export default async function RevenueChart() {
 
   return (
     <div className="w-full md:col-span-4">
-      <h2 className={` mb-4 text-xl md:text-2xl`}>
-        Recent Revenue
-      </h2>
+      <h2 className={` mb-4 text-xl md:text-2xl`}>Recent Revenue</h2>
       <div className="rounded-xl bg-gray-50 p-4">
         <div className="sm:grid-cols-13 mt-0 grid grid-cols-12 items-end gap-2 rounded-md bg-white p-4 md:gap-4">
           <div
@@ -31,7 +30,7 @@ export default async function RevenueChart() {
                 style={{
                   height: `${(chartHeight / topLabel) * month.revenue}px`,
                 }}
-              ></div>
+              />
               <p className="-rotate-90 text-sm text-gray-400 sm:rotate-0">
                 {month.month}
               </p>
diff --git a/src/app/[lang]/dashboard/components/server/sidenav.tsx b/src/app/[lang]/dashboard/components/server/sidenav.tsx
index bc396c4..e89a0e6 100644
--- a/src/app/[lang]/dashboard/components/server/sidenav.tsx
+++ b/src/app/[lang]/dashboard/components/server/sidenav.tsx
@@ -1,5 +1,5 @@
-import NavLinks from '@/app/[lang]/dashboard/components/client/nav-links/nav-links';
-import Link from 'next/link';
+import NavLinks from "@/app/[lang]/dashboard/components/client/nav-links/nav-links";
+import Link from "next/link";
 
 export default function SideNav() {
   return (
@@ -8,13 +8,11 @@ export default function SideNav() {
         className="mb-2 flex h-20 items-end justify-start rounded-md bg-blue-600 p-4 md:h-40"
         href="/"
       >
-        <div className="w-32 text-white md:w-40">
-          Home
-        </div>
+        <div className="w-32 text-white md:w-40">Home</div>
       </Link>
       <div className="flex grow flex-row justify-between space-x-2 md:flex-col md:space-x-0 md:space-y-2">
         <NavLinks />
-        <div className="hidden h-auto w-full grow rounded-md bg-gray-50 md:block"></div>
+        <div className="hidden h-auto w-full grow rounded-md bg-gray-50 md:block" />
       </div>
     </div>
   );
diff --git a/src/app/[lang]/dashboard/components/server/skeletons/skeletons.tsx b/src/app/[lang]/dashboard/components/server/skeletons/skeletons.tsx
index 52b8a87..c7b2e60 100644
--- a/src/app/[lang]/dashboard/components/server/skeletons/skeletons.tsx
+++ b/src/app/[lang]/dashboard/components/server/skeletons/skeletons.tsx
@@ -1,6 +1,6 @@
 // Loading animation
 const shimmer =
-  'before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/60 before:to-transparent';
+  "before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/60 before:to-transparent";
 
 export function CardSkeleton() {
   return (
@@ -106,33 +106,36 @@ export function TableRowSkeleton() {
   return (
     <tr className="w-full border-b border-gray-100 last-of-type:border-none [&:first-child>td:first-child]:rounded-tl-lg [&:first-child>td:last-child]:rounded-tr-lg [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg">
       {/* Customer Name and Image */}
-      <td className="relative overflow-hidden whitespace-nowrap py-3 pl-6 pr-3">
+      <td
+        aria-label="Customer name"
+        className="relative overflow-hidden whitespace-nowrap py-3 pl-6 pr-3"
+      >
         <div className="flex items-center gap-3">
-          <div className="h-8 w-8 rounded-full bg-gray-100"></div>
-          <div className="h-6 w-24 rounded bg-gray-100"></div>
+          <div className="h-8 w-8 rounded-full bg-gray-100" />
+          <div className="h-6 w-24 rounded bg-gray-100" />
         </div>
       </td>
       {/* Email */}
-      <td className="whitespace-nowrap px-3 py-3">
-        <div className="h-6 w-32 rounded bg-gray-100"></div>
+      <td aria-label="Email" className="whitespace-nowrap px-3 py-3">
+        <div className="h-6 w-32 rounded bg-gray-100" />
       </td>
       {/* Amount */}
-      <td className="whitespace-nowrap px-3 py-3">
-        <div className="h-6 w-16 rounded bg-gray-100"></div>
+      <td aria-label="Amount" className="whitespace-nowrap px-3 py-3">
+        <div className="h-6 w-16 rounded bg-gray-100" />
       </td>
       {/* Date */}
-      <td className="whitespace-nowrap px-3 py-3">
-        <div className="h-6 w-16 rounded bg-gray-100"></div>
+      <td aria-label="Date" className="whitespace-nowrap px-3 py-3">
+        <div className="h-6 w-16 rounded bg-gray-100" />
       </td>
       {/* Status */}
-      <td className="whitespace-nowrap px-3 py-3">
-        <div className="h-6 w-16 rounded bg-gray-100"></div>
+      <td aria-label="Status" className="whitespace-nowrap px-3 py-3">
+        <div className="h-6 w-16 rounded bg-gray-100" />
       </td>
       {/* Actions */}
-      <td className="whitespace-nowrap py-3 pl-6 pr-3">
+      <td aria-label="Actions" className="whitespace-nowrap py-3 pl-6 pr-3">
         <div className="flex justify-end gap-3">
-          <div className="h-[38px] w-[38px] rounded bg-gray-100"></div>
-          <div className="h-[38px] w-[38px] rounded bg-gray-100"></div>
+          <div className="h-[38px] w-[38px] rounded bg-gray-100" />
+          <div className="h-[38px] w-[38px] rounded bg-gray-100" />
         </div>
       </td>
     </tr>
@@ -144,19 +147,19 @@ export function InvoicesMobileSkeleton() {
     <div className="mb-2 w-full rounded-md bg-white p-4">
       <div className="flex items-center justify-between border-b border-gray-100 pb-8">
         <div className="flex items-center">
-          <div className="mr-2 h-8 w-8 rounded-full bg-gray-100"></div>
-          <div className="h-6 w-16 rounded bg-gray-100"></div>
+          <div className="mr-2 h-8 w-8 rounded-full bg-gray-100" />
+          <div className="h-6 w-16 rounded bg-gray-100" />
         </div>
-        <div className="h-6 w-16 rounded bg-gray-100"></div>
+        <div className="h-6 w-16 rounded bg-gray-100" />
       </div>
       <div className="flex w-full items-center justify-between pt-4">
         <div>
-          <div className="h-6 w-16 rounded bg-gray-100"></div>
-          <div className="mt-2 h-6 w-24 rounded bg-gray-100"></div>
+          <div className="h-6 w-16 rounded bg-gray-100" />
+          <div className="mt-2 h-6 w-24 rounded bg-gray-100" />
         </div>
         <div className="flex justify-end gap-2">
-          <div className="h-10 w-10 rounded bg-gray-100"></div>
-          <div className="h-10 w-10 rounded bg-gray-100"></div>
+          <div className="h-10 w-10 rounded bg-gray-100" />
+          <div className="h-10 w-10 rounded bg-gray-100" />
         </div>
       </div>
     </div>
diff --git a/src/app/[lang]/dashboard/layout.tsx b/src/app/[lang]/dashboard/layout.tsx
index 4e678cf..308e2a7 100644
--- a/src/app/[lang]/dashboard/layout.tsx
+++ b/src/app/[lang]/dashboard/layout.tsx
@@ -1,20 +1,22 @@
-"use client"
+"use client";
+
 import SideNav from "@/app/[lang]/dashboard/components/server/sidenav";
 import dashboardAppModule from "@/app/[lang]/dashboard/module/dashboard-app-module";
 import { DiContext } from "@/bootstrap/di/di-context";
 import { useRef } from "react";
 
- 
 export default function Layout({ children }: { children: React.ReactNode }) {
-  const di = useRef(dashboardAppModule())
+  const di = useRef(dashboardAppModule());
   return (
     <DiContext.Provider value={di.current}>
       <div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
         <div className="w-full flex-none md:w-64">
           <SideNav />
         </div>
-        <div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
+        <div className="flex-grow p-6 md:overflow-y-auto md:p-12">
+          {children}
+        </div>
       </div>
     </DiContext.Provider>
   );
-}
\ No newline at end of file
+}
diff --git a/src/app/[lang]/dashboard/loading.tsx b/src/app/[lang]/dashboard/loading.tsx
index 9c38c20..0002d3a 100644
--- a/src/app/[lang]/dashboard/loading.tsx
+++ b/src/app/[lang]/dashboard/loading.tsx
@@ -2,4 +2,4 @@ import DashboardSkeleton from "@/app/[lang]/dashboard/components/server/skeleton
 
 export default function Loading() {
   return <DashboardSkeleton />;
-}
\ No newline at end of file
+}
diff --git a/src/app/[lang]/dashboard/module/dashboard-app-module.ts b/src/app/[lang]/dashboard/module/dashboard-app-module.ts
index 10992a6..323a2f2 100644
--- a/src/app/[lang]/dashboard/module/dashboard-app-module.ts
+++ b/src/app/[lang]/dashboard/module/dashboard-app-module.ts
@@ -1,13 +1,16 @@
 import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm";
-import di from "@/bootstrap/di/init-di"
+import di from "@/bootstrap/di/init-di";
 import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
 
 export default function dashboardAppModule() {
-    const dashboardDi = di.createChildContainer()
-    
-    dashboardDi.register(createInvoiceUsecase.name, {
-        useValue: createInvoiceUsecase
-    })
-    dashboardDi.register(CreateRandomInvoiceButtonVM, CreateRandomInvoiceButtonVM)
-    return dashboardDi
+  const dashboardDi = di.createChildContainer();
+
+  dashboardDi.register(createInvoiceUsecase.name, {
+    useValue: createInvoiceUsecase,
+  });
+  dashboardDi.register(
+    CreateRandomInvoiceButtonVM,
+    CreateRandomInvoiceButtonVM,
+  );
+  return dashboardDi;
 }
diff --git a/src/app/[lang]/dashboard/page.tsx b/src/app/[lang]/dashboard/page.tsx
index 0035a7b..f9f980f 100644
--- a/src/app/[lang]/dashboard/page.tsx
+++ b/src/app/[lang]/dashboard/page.tsx
@@ -1,4 +1,7 @@
-import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/[lang]/dashboard/components/server/skeletons/skeletons";
+import {
+  LatestInvoicesSkeleton,
+  RevenueChartSkeleton,
+} from "@/app/[lang]/dashboard/components/server/skeletons/skeletons";
 import CardWrapper from "@/app/[lang]/dashboard/components/server/cards/cards";
 import LatestInvoices from "@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices";
 import RevenueChart from "@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart";
@@ -6,25 +9,28 @@ import { Suspense } from "react";
 import { getServerTranslation } from "@/bootstrap/i18n/i18n";
 import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
 
-export default async function Dashboard(props: {params: Promise<{lang: string}>}) {
-  const {lang} = await props.params 
-  const { t } = await getServerTranslation(lang)
+export default async function Dashboard(props: {
+  params: Promise<{ lang: string }>;
+}) {
+  const { params } = props;
+  const { lang } = await params;
+  const { t } = await getServerTranslation(lang);
   return (
     <main>
-      <h1 className={`mb-4 text-xl md:text-2xl`}>
-        {t(langKey.global.dashboard)} 
+      <h1 className="mb-4 text-xl md:text-2xl">
+        {t(langKey.global.dashboard)}
       </h1>
       <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
         <CardWrapper />
       </div>
       <div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
         <Suspense fallback={<RevenueChartSkeleton />}>
-          <RevenueChart  />
+          <RevenueChart />
         </Suspense>
         <Suspense fallback={<LatestInvoicesSkeleton />}>
           <LatestInvoices />
         </Suspense>
       </div>
     </main>
-  )
+  );
 }
diff --git a/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts b/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
index 136f5b0..44abdcb 100644
--- a/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
+++ b/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
@@ -8,39 +8,46 @@ import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-i
 import { faker } from "@faker-js/faker";
 import { useRouter } from "next/navigation";
 import { useTranslation } from "react-i18next";
+
 export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
-    private createInvoice: typeof createInvoiceUsecase
+  private createInvoice: typeof createInvoiceUsecase;
 
-    constructor() {
-        super()
-        this.createInvoice = this.di.resolve(createInvoiceUsecase.name)
-    }
+  constructor() {
+    super();
+    this.createInvoice = this.di.resolve(createInvoiceUsecase.name);
+  }
 
-    useVM(): ButtonVm {
-        const router = useRouter()
-        const [action, isPending] = useServerAction(() => this.onClickHandler(router.refresh))
-        const throttledOnClick = useThrottle(action, 5000)
+  useVM(): ButtonVm {
+    const router = useRouter();
+    const [action, isPending] = useServerAction(() =>
+      this.onClickHandler(router.refresh),
+    );
+    const throttledOnClick = useThrottle(action, 5000);
 
-        const {t} = useTranslation()
-        
-        return {
-            props: {
-                title: t(isPending ? langKey.global.loading : langKey.dashboard.invoice.createButton),
-                isDisable: isPending ? true : false
-            },
-            onClick: throttledOnClick.bind(this)
-        }
-    }
+    const { t } = useTranslation();
 
-    async onClickHandler(refreshPage: () => void) {
-        const fakedParams: InvoiceParam = {
-            amount: faker.number.int({
-                min: 1,
-                max: 10
-            }),
-            status: "paid"
-        }
-        await this.createInvoice(fakedParams)
-        refreshPage()
-    }
-} 
\ No newline at end of file
+    return {
+      props: {
+        title: t(
+          isPending
+            ? langKey.global.loading
+            : langKey.dashboard.invoice.createButton,
+        ),
+        isDisable: !!isPending,
+      },
+      onClick: throttledOnClick.bind(this),
+    };
+  }
+
+  async onClickHandler(refreshPage: () => void) {
+    const fakedParams: InvoiceParam = {
+      amount: faker.number.int({
+        min: 1,
+        max: 10,
+      }),
+      status: "paid",
+    };
+    await this.createInvoice(fakedParams);
+    refreshPage();
+  }
+}
diff --git a/src/app/[lang]/layout.tsx b/src/app/[lang]/layout.tsx
index dedc58e..4fbaf40 100644
--- a/src/app/[lang]/layout.tsx
+++ b/src/app/[lang]/layout.tsx
@@ -2,8 +2,14 @@ import { initI18next } from "@/bootstrap/i18n/i18n";
 import TranslationsProvider from "@/bootstrap/i18n/i18n-provider";
 import { PropsWithChildren } from "react";
 
-export default async function layout(props: PropsWithChildren & {params: Promise<{lang: string}>}) {
-    const lang = (await props.params).lang
-    const { resources} = await initI18next({lng: lang})
-    return <TranslationsProvider lng={lang} resources={resources}>{props.children}</TranslationsProvider>
-}
\ No newline at end of file
+export default async function layout(
+  props: PropsWithChildren & { params: Promise<{ lang: string }> },
+) {
+  const { lang } = await props.params;
+  const { resources } = await initI18next({ lng: lang });
+  return (
+    <TranslationsProvider lng={lang} resources={resources}>
+      {props.children}
+    </TranslationsProvider>
+  );
+}
diff --git a/src/app/[lang]/page.tsx b/src/app/[lang]/page.tsx
index b34fd95..1e9c77c 100644
--- a/src/app/[lang]/page.tsx
+++ b/src/app/[lang]/page.tsx
@@ -1,14 +1,12 @@
-
 export default function Home() {
   return (
     <main className="flex min-h-screen flex-col p-6">
       <div className="mt-4 flex grow flex-col gap-4 md:flex-row">
         <div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
-        <div />
-          <p className={`text-xl text-gray-800 md:text-3xl md:leading-normal`}>
-            <strong>Welcome to Acme.</strong> This is the example for the{' '}
-           
-            , brought to you by Vercel.
+          <div />
+          <p className="text-xl text-gray-800 md:text-3xl md:leading-normal">
+            <strong>Welcome to Acme.</strong> This is the example for the ,
+            brought to you by Vercel.
           </p>
         </div>
       </div>
diff --git a/src/app/components/button/button-vm.ts b/src/app/components/button/button-vm.ts
index ef0d7af..fa23606 100644
--- a/src/app/components/button/button-vm.ts
+++ b/src/app/components/button/button-vm.ts
@@ -1,7 +1,7 @@
 export default interface ButtonVm {
-    props: {
-        title: string;
-        isDisable: boolean;
-    }
-    onClick(): void
-}
\ No newline at end of file
+  props: {
+    title: string;
+    isDisable: boolean;
+  };
+  onClick(): void;
+}
diff --git a/src/app/components/button/button.tsx b/src/app/components/button/button.tsx
index df0de5b..5520728 100644
--- a/src/app/components/button/button.tsx
+++ b/src/app/components/button/button.tsx
@@ -1,20 +1,24 @@
-"use client"
+"use client";
+
 import BaseView, { BuildProps } from "@/bootstrap/helpers/view/base-view";
 import ButtonVm from "@/app/components/button/button-vm";
 import { ReactNode } from "react";
-import * as React from "react"
-import { Slot } from "@radix-ui/react-slot"
-import { cva, type VariantProps } from "class-variance-authority"
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
 import { cn } from "@/bootstrap/helpers/lib/ui-utils";
 
 export default class Button extends BaseView<ButtonVm> {
-    protected Build(props: BuildProps<ButtonVm>): ReactNode {
-        const {vm} = props
-        
-        return <ButtonUi disabled={vm.props.isDisable} onClick={vm.onClick} >{vm.props.title}</ButtonUi> 
-    }
-}
+  protected Build(props: BuildProps<ButtonVm>): ReactNode {
+    const { vm } = props;
 
+    return (
+      <ButtonUi disabled={vm.props.isDisable} onClick={vm.onClick}>
+        {vm.props.title}
+      </ButtonUi>
+    );
+  }
+}
 
 const buttonVariants = cva(
   "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
@@ -43,27 +47,28 @@ const buttonVariants = cva(
       variant: "default",
       size: "default",
     },
-  }
-)
+  },
+);
 
 export interface ButtonProps
   extends React.ButtonHTMLAttributes<HTMLButtonElement>,
     VariantProps<typeof buttonVariants> {
-  asChild?: boolean
+  asChild?: boolean;
 }
 
 const ButtonUi = React.forwardRef<HTMLButtonElement, ButtonProps>(
   ({ className, variant, size, asChild = false, ...props }, ref) => {
-    const Comp = asChild ? Slot : "button"
+    const Comp = asChild ? Slot : "button";
     return (
       <Comp
         className={cn(buttonVariants({ variant, size, className }))}
         ref={ref}
+        // eslint-disable-next-line react/jsx-props-no-spreading
         {...props}
       />
-    )
-  }
-)
-ButtonUi.displayName = "Button"
+    );
+  },
+);
+ButtonUi.displayName = "Button";
 
-export { buttonVariants }
\ No newline at end of file
+export { buttonVariants };
diff --git a/src/app/components/icons/document.tsx b/src/app/components/icons/document.tsx
index a8cbcb6..eeb669e 100644
--- a/src/app/components/icons/document.tsx
+++ b/src/app/components/icons/document.tsx
@@ -1,5 +1,20 @@
-export function DocumentIcon(props: {className?: string}) {
-    return (
-        <svg className={props.className} width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 2.5C3 2.22386 3.22386 2 3.5 2H9.08579C9.21839 2 9.34557 2.05268 9.43934 2.14645L11.8536 4.56066C11.9473 4.65443 12 4.78161 12 4.91421V12.5C12 12.7761 11.7761 13 11.5 13H3.5C3.22386 13 3 12.7761 3 12.5V2.5ZM3.5 1C2.67157 1 2 1.67157 2 2.5V12.5C2 13.3284 2.67157 14 3.5 14H11.5C12.3284 14 13 13.3284 13 12.5V4.91421C13 4.51639 12.842 4.13486 12.5607 3.85355L10.1464 1.43934C9.86514 1.15804 9.48361 1 9.08579 1H3.5ZM4.5 4C4.22386 4 4 4.22386 4 4.5C4 4.77614 4.22386 5 4.5 5H7.5C7.77614 5 8 4.77614 8 4.5C8 4.22386 7.77614 4 7.5 4H4.5ZM4.5 7C4.22386 7 4 7.22386 4 7.5C4 7.77614 4.22386 8 4.5 8H10.5C10.7761 8 11 7.77614 11 7.5C11 7.22386 10.7761 7 10.5 7H4.5ZM4.5 10C4.22386 10 4 10.2239 4 10.5C4 10.7761 4.22386 11 4.5 11H10.5C10.7761 11 11 10.7761 11 10.5C11 10.2239 10.7761 10 10.5 10H4.5Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg>
-    )
-}
\ No newline at end of file
+export function DocumentIcon(props: { className?: string }) {
+  const { className } = props;
+  return (
+    <svg
+      className={className}
+      width="15"
+      height="15"
+      viewBox="0 0 15 15"
+      fill="none"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <path
+        d="M3 2.5C3 2.22386 3.22386 2 3.5 2H9.08579C9.21839 2 9.34557 2.05268 9.43934 2.14645L11.8536 4.56066C11.9473 4.65443 12 4.78161 12 4.91421V12.5C12 12.7761 11.7761 13 11.5 13H3.5C3.22386 13 3 12.7761 3 12.5V2.5ZM3.5 1C2.67157 1 2 1.67157 2 2.5V12.5C2 13.3284 2.67157 14 3.5 14H11.5C12.3284 14 13 13.3284 13 12.5V4.91421C13 4.51639 12.842 4.13486 12.5607 3.85355L10.1464 1.43934C9.86514 1.15804 9.48361 1 9.08579 1H3.5ZM4.5 4C4.22386 4 4 4.22386 4 4.5C4 4.77614 4.22386 5 4.5 5H7.5C7.77614 5 8 4.77614 8 4.5C8 4.22386 7.77614 4 7.5 4H4.5ZM4.5 7C4.22386 7 4 7.22386 4 7.5C4 7.77614 4.22386 8 4.5 8H10.5C10.7761 8 11 7.77614 11 7.5C11 7.22386 10.7761 7 10.5 7H4.5ZM4.5 10C4.22386 10 4 10.2239 4 10.5C4 10.7761 4.22386 11 4.5 11H10.5C10.7761 11 11 10.7761 11 10.5C11 10.2239 10.7761 10 10.5 10H4.5Z"
+        fill="currentColor"
+        fillRule="evenodd"
+        clipRule="evenodd"
+      />
+    </svg>
+  );
+}
diff --git a/src/app/components/icons/home.tsx b/src/app/components/icons/home.tsx
index 8270d4e..ac7a258 100644
--- a/src/app/components/icons/home.tsx
+++ b/src/app/components/icons/home.tsx
@@ -1,5 +1,20 @@
-export default function HomeIcon(props: {className?: string}) {
-    return (
-        <svg className={props.className} width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.07926 0.222253C7.31275 -0.007434 7.6873 -0.007434 7.92079 0.222253L14.6708 6.86227C14.907 7.09465 14.9101 7.47453 14.6778 7.71076C14.4454 7.947 14.0655 7.95012 13.8293 7.71773L13 6.90201V12.5C13 12.7761 12.7762 13 12.5 13H2.50002C2.22388 13 2.00002 12.7761 2.00002 12.5V6.90201L1.17079 7.71773C0.934558 7.95012 0.554672 7.947 0.32229 7.71076C0.0899079 7.47453 0.0930283 7.09465 0.32926 6.86227L7.07926 0.222253ZM7.50002 1.49163L12 5.91831V12H10V8.49999C10 8.22385 9.77617 7.99999 9.50002 7.99999H6.50002C6.22388 7.99999 6.00002 8.22385 6.00002 8.49999V12H3.00002V5.91831L7.50002 1.49163ZM7.00002 12H9.00002V8.99999H7.00002V12Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg>
-    )
-}
\ No newline at end of file
+export default function HomeIcon(props: { className?: string }) {
+  const { className } = props;
+  return (
+    <svg
+      className={className}
+      width="15"
+      height="15"
+      viewBox="0 0 15 15"
+      fill="none"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <path
+        d="M7.07926 0.222253C7.31275 -0.007434 7.6873 -0.007434 7.92079 0.222253L14.6708 6.86227C14.907 7.09465 14.9101 7.47453 14.6778 7.71076C14.4454 7.947 14.0655 7.95012 13.8293 7.71773L13 6.90201V12.5C13 12.7761 12.7762 13 12.5 13H2.50002C2.22388 13 2.00002 12.7761 2.00002 12.5V6.90201L1.17079 7.71773C0.934558 7.95012 0.554672 7.947 0.32229 7.71076C0.0899079 7.47453 0.0930283 7.09465 0.32926 6.86227L7.07926 0.222253ZM7.50002 1.49163L12 5.91831V12H10V8.49999C10 8.22385 9.77617 7.99999 9.50002 7.99999H6.50002C6.22388 7.99999 6.00002 8.22385 6.00002 8.49999V12H3.00002V5.91831L7.50002 1.49163ZM7.00002 12H9.00002V8.99999H7.00002V12Z"
+        fill="currentColor"
+        fillRule="evenodd"
+        clipRule="evenodd"
+      />
+    </svg>
+  );
+}
diff --git a/src/app/components/icons/user.tsx b/src/app/components/icons/user.tsx
index 02b56ed..dcd7f93 100644
--- a/src/app/components/icons/user.tsx
+++ b/src/app/components/icons/user.tsx
@@ -1,7 +1,19 @@
-export function UserIcon(props: {className?: string}) {
-    return (
-        <svg className={props.className} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
-            <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
-        </svg>
-    )
-}
\ No newline at end of file
+export function UserIcon(props: { className?: string }) {
+  const { className } = props;
+  return (
+    <svg
+      className={className}
+      xmlns="http://www.w3.org/2000/svg"
+      fill="none"
+      viewBox="0 0 24 24"
+      strokeWidth={1.5}
+      stroke="currentColor"
+    >
+      <path
+        strokeLinecap="round"
+        strokeLinejoin="round"
+        d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z"
+      />
+    </svg>
+  );
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 8a8df4e..a36cde0 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,6 +1,7 @@
 import type { Metadata } from "next";
 import localFont from "next/font/local";
 import "./globals.css";
+
 const geistSans = localFont({
   src: "./fonts/GeistVF.woff",
   variable: "--font-geist-sans",
@@ -23,7 +24,7 @@ export default function RootLayout({
   children: React.ReactNode;
 }>) {
   return (
-    <html>
+    <html lang="en">
       <body
         className={`${geistSans.variable} ${geistMono.variable} antialiased`}
       >
diff --git a/src/bootstrap/boundaries/db/db.ts b/src/bootstrap/boundaries/db/db.ts
index d6962af..2757311 100644
--- a/src/bootstrap/boundaries/db/db.ts
+++ b/src/bootstrap/boundaries/db/db.ts
@@ -1,13 +1,12 @@
 import postgres from "postgres";
 
-
 const envs = process.env;
 const dbConfigs = {
-    host: envs.POSTGRES_HOST,
-    port: Number(envs.POSTGRES_PORT),
-    username: envs.POSTGRES_USER,
-    password: envs.POSTGRES_PASS,
-    database: envs.POSTGRES_DB,
-}
+  host: envs.POSTGRES_HOST,
+  port: Number(envs.POSTGRES_PORT),
+  username: envs.POSTGRES_USER,
+  password: envs.POSTGRES_PASS,
+  database: envs.POSTGRES_DB,
+};
 
 export const sql = postgres(dbConfigs);
diff --git a/src/bootstrap/boundaries/db/placeholder-data.js b/src/bootstrap/boundaries/db/placeholder-data.js
index 15a4156..be007ee 100644
--- a/src/bootstrap/boundaries/db/placeholder-data.js
+++ b/src/bootstrap/boundaries/db/placeholder-data.js
@@ -2,73 +2,73 @@
 // https://nextjs.org/learn/dashboard-app/fetching-data
 const users = [
   {
-    id: '410544b2-4001-4271-9855-fec4b6a6442a',
-    name: 'User',
-    email: 'user@nextmail.com',
-    password: '123456',
+    id: "410544b2-4001-4271-9855-fec4b6a6442a",
+    name: "User",
+    email: "user@nextmail.com",
+    password: "123456",
   },
 ];
 
 const customers = [
   {
-    id: '3958dc9e-712f-4377-85e9-fec4b6a6442a',
-    name: 'Delba de Oliveira',
-    email: 'delba@oliveira.com',
-    image_url: '/customers/delba-de-oliveira.png',
+    id: "3958dc9e-712f-4377-85e9-fec4b6a6442a",
+    name: "Delba de Oliveira",
+    email: "delba@oliveira.com",
+    image_url: "/customers/delba-de-oliveira.png",
   },
   {
-    id: '3958dc9e-742f-4377-85e9-fec4b6a6442a',
-    name: 'Lee Robinson',
-    email: 'lee@robinson.com',
-    image_url: '/customers/lee-robinson.png',
+    id: "3958dc9e-742f-4377-85e9-fec4b6a6442a",
+    name: "Lee Robinson",
+    email: "lee@robinson.com",
+    image_url: "/customers/lee-robinson.png",
   },
   {
-    id: '3958dc9e-737f-4377-85e9-fec4b6a6442a',
-    name: 'Hector Simpson',
-    email: 'hector@simpson.com',
-    image_url: '/customers/hector-simpson.png',
+    id: "3958dc9e-737f-4377-85e9-fec4b6a6442a",
+    name: "Hector Simpson",
+    email: "hector@simpson.com",
+    image_url: "/customers/hector-simpson.png",
   },
   {
-    id: '50ca3e18-62cd-11ee-8c99-0242ac120002',
-    name: 'Steven Tey',
-    email: 'steven@tey.com',
-    image_url: '/customers/steven-tey.png',
+    id: "50ca3e18-62cd-11ee-8c99-0242ac120002",
+    name: "Steven Tey",
+    email: "steven@tey.com",
+    image_url: "/customers/steven-tey.png",
   },
   {
-    id: '3958dc9e-787f-4377-85e9-fec4b6a6442a',
-    name: 'Steph Dietz',
-    email: 'steph@dietz.com',
-    image_url: '/customers/steph-dietz.png',
+    id: "3958dc9e-787f-4377-85e9-fec4b6a6442a",
+    name: "Steph Dietz",
+    email: "steph@dietz.com",
+    image_url: "/customers/steph-dietz.png",
   },
   {
-    id: '76d65c26-f784-44a2-ac19-586678f7c2f2',
-    name: 'Michael Novotny',
-    email: 'michael@novotny.com',
-    image_url: '/customers/michael-novotny.png',
+    id: "76d65c26-f784-44a2-ac19-586678f7c2f2",
+    name: "Michael Novotny",
+    email: "michael@novotny.com",
+    image_url: "/customers/michael-novotny.png",
   },
   {
-    id: 'd6e15727-9fe1-4961-8c5b-ea44a9bd81aa',
-    name: 'Evil Rabbit',
-    email: 'evil@rabbit.com',
-    image_url: '/customers/evil-rabbit.png',
+    id: "d6e15727-9fe1-4961-8c5b-ea44a9bd81aa",
+    name: "Evil Rabbit",
+    email: "evil@rabbit.com",
+    image_url: "/customers/evil-rabbit.png",
   },
   {
-    id: '126eed9c-c90c-4ef6-a4a8-fcf7408d3c66',
-    name: 'Emil Kowalski',
-    email: 'emil@kowalski.com',
-    image_url: '/customers/emil-kowalski.png',
+    id: "126eed9c-c90c-4ef6-a4a8-fcf7408d3c66",
+    name: "Emil Kowalski",
+    email: "emil@kowalski.com",
+    image_url: "/customers/emil-kowalski.png",
   },
   {
-    id: 'CC27C14A-0ACF-4F4A-A6C9-D45682C144B9',
-    name: 'Amy Burns',
-    email: 'amy@burns.com',
-    image_url: '/customers/amy-burns.png',
+    id: "CC27C14A-0ACF-4F4A-A6C9-D45682C144B9",
+    name: "Amy Burns",
+    email: "amy@burns.com",
+    image_url: "/customers/amy-burns.png",
   },
   {
-    id: '13D07535-C59E-4157-A011-F8D2EF4E0CBB',
-    name: 'Balazs Orban',
-    email: 'balazs@orban.com',
-    image_url: '/customers/balazs-orban.png',
+    id: "13D07535-C59E-4157-A011-F8D2EF4E0CBB",
+    name: "Balazs Orban",
+    email: "balazs@orban.com",
+    image_url: "/customers/balazs-orban.png",
   },
 ];
 
@@ -76,108 +76,108 @@ const invoices = [
   {
     customer_id: customers[0].id,
     amount: 15795,
-    status: 'pending',
-    date: '2022-12-06',
+    status: "pending",
+    date: "2022-12-06",
   },
   {
     customer_id: customers[1].id,
     amount: 20348,
-    status: 'pending',
-    date: '2022-11-14',
+    status: "pending",
+    date: "2022-11-14",
   },
   {
     customer_id: customers[4].id,
     amount: 3040,
-    status: 'paid',
-    date: '2022-10-29',
+    status: "paid",
+    date: "2022-10-29",
   },
   {
     customer_id: customers[3].id,
     amount: 44800,
-    status: 'paid',
-    date: '2023-09-10',
+    status: "paid",
+    date: "2023-09-10",
   },
   {
     customer_id: customers[5].id,
     amount: 34577,
-    status: 'pending',
-    date: '2023-08-05',
+    status: "pending",
+    date: "2023-08-05",
   },
   {
     customer_id: customers[7].id,
     amount: 54246,
-    status: 'pending',
-    date: '2023-07-16',
+    status: "pending",
+    date: "2023-07-16",
   },
   {
     customer_id: customers[6].id,
     amount: 666,
-    status: 'pending',
-    date: '2023-06-27',
+    status: "pending",
+    date: "2023-06-27",
   },
   {
     customer_id: customers[3].id,
     amount: 32545,
-    status: 'paid',
-    date: '2023-06-09',
+    status: "paid",
+    date: "2023-06-09",
   },
   {
     customer_id: customers[4].id,
     amount: 1250,
-    status: 'paid',
-    date: '2023-06-17',
+    status: "paid",
+    date: "2023-06-17",
   },
   {
     customer_id: customers[5].id,
     amount: 8546,
-    status: 'paid',
-    date: '2023-06-07',
+    status: "paid",
+    date: "2023-06-07",
   },
   {
     customer_id: customers[1].id,
     amount: 500,
-    status: 'paid',
-    date: '2023-08-19',
+    status: "paid",
+    date: "2023-08-19",
   },
   {
     customer_id: customers[5].id,
     amount: 8945,
-    status: 'paid',
-    date: '2023-06-03',
+    status: "paid",
+    date: "2023-06-03",
   },
   {
     customer_id: customers[2].id,
     amount: 8945,
-    status: 'paid',
-    date: '2023-06-18',
+    status: "paid",
+    date: "2023-06-18",
   },
   {
     customer_id: customers[0].id,
     amount: 8945,
-    status: 'paid',
-    date: '2023-10-04',
+    status: "paid",
+    date: "2023-10-04",
   },
   {
     customer_id: customers[2].id,
     amount: 1000,
-    status: 'paid',
-    date: '2022-06-05',
+    status: "paid",
+    date: "2022-06-05",
   },
 ];
 
 const revenue = [
-  { month: 'Jan', revenue: 2000 },
-  { month: 'Feb', revenue: 1800 },
-  { month: 'Mar', revenue: 2200 },
-  { month: 'Apr', revenue: 2500 },
-  { month: 'May', revenue: 2300 },
-  { month: 'Jun', revenue: 3200 },
-  { month: 'Jul', revenue: 3500 },
-  { month: 'Aug', revenue: 3700 },
-  { month: 'Sep', revenue: 2500 },
-  { month: 'Oct', revenue: 2800 },
-  { month: 'Nov', revenue: 3000 },
-  { month: 'Dec', revenue: 4800 },
+  { month: "Jan", revenue: 2000 },
+  { month: "Feb", revenue: 1800 },
+  { month: "Mar", revenue: 2200 },
+  { month: "Apr", revenue: 2500 },
+  { month: "May", revenue: 2300 },
+  { month: "Jun", revenue: 3200 },
+  { month: "Jul", revenue: 3500 },
+  { month: "Aug", revenue: 3700 },
+  { month: "Sep", revenue: 2500 },
+  { month: "Oct", revenue: 2800 },
+  { month: "Nov", revenue: 3000 },
+  { month: "Dec", revenue: 4800 },
 ];
 
 module.exports = {
diff --git a/src/bootstrap/boundaries/db/seed.js b/src/bootstrap/boundaries/db/seed.js
index 1b4273f..530caf5 100644
--- a/src/bootstrap/boundaries/db/seed.js
+++ b/src/bootstrap/boundaries/db/seed.js
@@ -1,12 +1,14 @@
+/* eslint-disable no-console */
 /* eslint-disable @typescript-eslint/no-require-imports */
+const bcrypt = require("bcrypt");
+const postgres = require("postgres");
 const {
   invoices,
   customers,
   revenue,
   users,
-} = require('./placeholder-data.js');
-const bcrypt = require('bcrypt');
-const postgres = require('postgres');
+  // eslint-disable-next-line import/extensions
+} = require("./placeholder-data.js");
 
 async function seedUsers(sql) {
   try {
@@ -42,7 +44,7 @@ async function seedUsers(sql) {
       users: insertedUsers,
     };
   } catch (error) {
-    console.error('Error seeding users:', error);
+    console.error("Error seeding users:", error);
     throw error;
   }
 }
@@ -82,7 +84,7 @@ async function seedInvoices(sql) {
       invoices: insertedInvoices,
     };
   } catch (error) {
-    console.error('Error seeding invoices:', error);
+    console.error("Error seeding invoices:", error);
     throw error;
   }
 }
@@ -121,7 +123,7 @@ async function seedCustomers(sql) {
       customers: insertedCustomers,
     };
   } catch (error) {
-    console.error('Error seeding customers:', error);
+    console.error("Error seeding customers:", error);
     throw error;
   }
 }
@@ -156,7 +158,7 @@ async function seedRevenue(sql) {
       revenue: insertedRevenue,
     };
   } catch (error) {
-    console.error('Error seeding revenue:', error);
+    console.error("Error seeding revenue:", error);
     throw error;
   }
 }
@@ -169,7 +171,7 @@ async function main() {
     username: envs.POSTGRES_USER,
     password: envs.POSTGRES_PASS,
     database: envs.POSTGRES_DB,
-  }
+  };
 
   const sql = postgres(dbConfigs);
 
@@ -177,12 +179,11 @@ async function main() {
   await seedCustomers(sql);
   await seedInvoices(sql);
   await seedRevenue(sql);
-
 }
 
 main().catch((err) => {
   console.error(
-    'An error occurred while attempting to seed the database:',
+    "An error occurred while attempting to seed the database:",
     err,
   );
 });
diff --git a/src/bootstrap/di/di-context.tsx b/src/bootstrap/di/di-context.tsx
index 2474156..3505eff 100644
--- a/src/bootstrap/di/di-context.tsx
+++ b/src/bootstrap/di/di-context.tsx
@@ -1,21 +1,19 @@
-"use client"
+"use client";
+
 import di from "@/bootstrap/di/init-di";
 import { createContext, use } from "react";
 import { DependencyContainer } from "tsyringe";
 
-const DiContext = createContext<null | DependencyContainer>(di)
+const DiContext = createContext<null | DependencyContainer>(di);
 
 const useDI = () => {
-    const di = use(DiContext)
+  const di = use(DiContext);
 
-    if (!di) {
-        throw new Error("Di has not provided")
-    }
+  if (!di) {
+    throw new Error("Di has not provided");
+  }
 
-    return di
-}
+  return di;
+};
 
-export {
-    DiContext,
-    useDI,
-}
\ No newline at end of file
+export { DiContext, useDI };
diff --git a/src/bootstrap/di/init-di.ts b/src/bootstrap/di/init-di.ts
index 3ebdeef..4bf4261 100644
--- a/src/bootstrap/di/init-di.ts
+++ b/src/bootstrap/di/init-di.ts
@@ -1,5 +1,5 @@
 // "use client"
-import "reflect-metadata"
+import "reflect-metadata";
 import { container, DependencyContainer } from "tsyringe";
 
 /**
@@ -15,4 +15,4 @@ const InitDI = (): DependencyContainer => {
 
 const di = InitDI();
 
-export default di;
\ No newline at end of file
+export default di;
diff --git a/src/bootstrap/helpers/global-helpers.ts b/src/bootstrap/helpers/global-helpers.ts
index cf37914..ca617a8 100644
--- a/src/bootstrap/helpers/global-helpers.ts
+++ b/src/bootstrap/helpers/global-helpers.ts
@@ -1,2 +1 @@
-
-export const isServer = typeof window === 'undefined'
\ No newline at end of file
+export const isServer = typeof window === "undefined";
diff --git a/src/bootstrap/helpers/hooks/use-server-action.ts b/src/bootstrap/helpers/hooks/use-server-action.ts
index abb7516..a4a5034 100644
--- a/src/bootstrap/helpers/hooks/use-server-action.ts
+++ b/src/bootstrap/helpers/hooks/use-server-action.ts
@@ -1,11 +1,11 @@
-import { useState, useEffect, useTransition, useRef } from 'react';
+import { useState, useEffect, useTransition, useRef } from "react";
 
 /**
- * 
+ *
  * @param action Main server action to run
  * @param onFinished Callback to run after action
  * @returns transitioned action to run and is pending variable
-*/
+ */
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 export const useServerAction = <P extends any[], R>(
   action: (...args: P) => Promise<R>,
@@ -37,4 +37,4 @@ export const useServerAction = <P extends any[], R>(
   };
 
   return [runAction, isPending];
-};
\ No newline at end of file
+};
diff --git a/src/bootstrap/helpers/hooks/use-throttle.ts b/src/bootstrap/helpers/hooks/use-throttle.ts
index ff2344f..6f8213b 100644
--- a/src/bootstrap/helpers/hooks/use-throttle.ts
+++ b/src/bootstrap/helpers/hooks/use-throttle.ts
@@ -1,18 +1,22 @@
-"use client"
+"use client";
 
-import { useEffect, useRef } from "react"
+import { useRef } from "react";
 
 /**
- * 
- * @param callback 
+ *
+ * @param callback
  * @param time In miliseconds
  */
-export default function useThrottle<T extends Function>(callback: T, time: number = 2000) {
-    const lastRun = useRef(Date.now())
+export default function useThrottle<T extends () => unknown>(
+  callback: T,
+  time: number = 2000,
+) {
+  const lastRun = useRef(Date.now());
 
-    return function() {
-        if (Date.now() - lastRun.current <= time) return;
-        lastRun.current = Date.now()
-        return callback()
-    }
-}
\ No newline at end of file
+  // eslint-disable-next-line func-names
+  return function () {
+    if (Date.now() - lastRun.current <= time) return;
+    lastRun.current = Date.now();
+    callback();
+  };
+}
diff --git a/src/bootstrap/helpers/lib/ui-utils.ts b/src/bootstrap/helpers/lib/ui-utils.ts
index bd0c391..a5ef193 100644
--- a/src/bootstrap/helpers/lib/ui-utils.ts
+++ b/src/bootstrap/helpers/lib/ui-utils.ts
@@ -1,6 +1,6 @@
-import { clsx, type ClassValue } from "clsx"
-import { twMerge } from "tailwind-merge"
+import { clsx, type ClassValue } from "clsx";
+import { twMerge } from "tailwind-merge";
 
 export function cn(...inputs: ClassValue[]) {
-  return twMerge(clsx(inputs))
+  return twMerge(clsx(inputs));
 }
diff --git a/src/bootstrap/helpers/type-helper.ts b/src/bootstrap/helpers/type-helper.ts
index c484e86..a1effb7 100644
--- a/src/bootstrap/helpers/type-helper.ts
+++ b/src/bootstrap/helpers/type-helper.ts
@@ -5,4 +5,4 @@ type Forbidden = { [_]: typeof _ };
 /**
  * You can use this type to make your parent class method forbidden to overwrite
  */
-export type NoOverride<T = void> = T & Forbidden;
\ No newline at end of file
+export type NoOverride<T = void> = T & Forbidden;
diff --git a/src/bootstrap/helpers/view/base-view.tsx b/src/bootstrap/helpers/view/base-view.tsx
index 990cd19..b0d4f57 100644
--- a/src/bootstrap/helpers/view/base-view.tsx
+++ b/src/bootstrap/helpers/view/base-view.tsx
@@ -1,4 +1,5 @@
-"use client"
+"use client";
+
 // import gdi from "@/bootstrap/di/init-di";
 /* eslint-disable react/display-name */
 /* eslint-disable @typescript-eslint/no-explicit-any */
@@ -23,7 +24,7 @@ const VvmConnector = memo(
   <IVM, PROPS>(props: IVvmConnector<IVM, PROPS>) => {
     const { View, Vm, restProps, children } = props;
 
-    const vm = Vm.useVM()
+    const vm = Vm.useVM();
 
     const allProps = {
       restProps,
@@ -69,17 +70,17 @@ export default abstract class BaseView<
   IVM extends IVMParent,
   PROPS extends IPropParent = undefined,
 > extends Component<BaseProps<IVM, PROPS>> {
-  protected abstract Build(props: BuildProps<IVM, PROPS>): ReactNode;
-
   protected get componentName() {
-    return this.constructor.name
+    return this.constructor.name;
   }
 
+  protected abstract Build(props: BuildProps<IVM, PROPS>): ReactNode;
+
   render(): ReactNode {
     const { vm, restProps, memoizedByVM, children, ...rest } = this.props;
-    
-    VvmConnector.displayName = this.componentName
-  
+
+    VvmConnector.displayName = this.componentName;
+
     return (
       <VvmConnector
         View={this.Build}
diff --git a/src/bootstrap/helpers/vm/base-vm.ts b/src/bootstrap/helpers/vm/base-vm.ts
index b9bb379..003443c 100644
--- a/src/bootstrap/helpers/vm/base-vm.ts
+++ b/src/bootstrap/helpers/vm/base-vm.ts
@@ -1,4 +1,5 @@
-"use client"
+"use client";
+
 import { useDI } from "@/bootstrap/di/di-context";
 import { NoOverride } from "@/bootstrap/helpers/type-helper";
 import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
@@ -31,8 +32,9 @@ export default abstract class BaseVM<
 
   /* -------------------------------------------------------------------------- */
   protected get di() {
-    return useDI()
+    return useDI();
   }
+
   /* -------------------------------------------------------------------------- */
   /**
    * You can use this hook in your useVm method to get rerender method
diff --git a/src/bootstrap/i18n/dictionaries/en.ts b/src/bootstrap/i18n/dictionaries/en.ts
index 3f0f60c..1c8313b 100644
--- a/src/bootstrap/i18n/dictionaries/en.ts
+++ b/src/bootstrap/i18n/dictionaries/en.ts
@@ -1,16 +1,16 @@
-import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
+import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
 
 const en: typeof langKey = {
-    global: {
-        home: "Home",
-        loading: "Loading",
-        dashboard: "Dashboard"
+  global: {
+    home: "Home",
+    loading: "Loading",
+    dashboard: "Dashboard",
+  },
+  dashboard: {
+    invoice: {
+      createButton: "Create random Invoice",
     },
-    dashboard: {
-        invoice: {
-            createButton: "Create random Invoice"
-        }
-    }
-}
+  },
+};
 
-export default en
\ No newline at end of file
+export default en;
diff --git a/src/bootstrap/i18n/dictionaries/lang-key.ts b/src/bootstrap/i18n/dictionaries/lang-key.ts
index 69ca04e..1bc2143 100644
--- a/src/bootstrap/i18n/dictionaries/lang-key.ts
+++ b/src/bootstrap/i18n/dictionaries/lang-key.ts
@@ -1,14 +1,14 @@
 const langKey = {
-    global: {
-        home: "global.home",
-        dashboard: "global.dashboard",
-        loading: "global.loading"
+  global: {
+    home: "global.home",
+    dashboard: "global.dashboard",
+    loading: "global.loading",
+  },
+  dashboard: {
+    invoice: {
+      createButton: "dashboard.invoice.createButton",
     },
-    dashboard: {
-        invoice: {
-            createButton: "dashboard.invoice.createButton"
-        }
-    }
-}
+  },
+};
 
-export default langKey;
\ No newline at end of file
+export default langKey;
diff --git a/src/bootstrap/i18n/dictionaries/ru.ts b/src/bootstrap/i18n/dictionaries/ru.ts
index 9c8e561..48b6221 100644
--- a/src/bootstrap/i18n/dictionaries/ru.ts
+++ b/src/bootstrap/i18n/dictionaries/ru.ts
@@ -1,16 +1,16 @@
-import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
+import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
 
 const ru: typeof langKey = {
-    global: {
-        home: "Дом",
-        loading: "Загрузка",
-        dashboard: "Панель приборов"
+  global: {
+    home: "Дом",
+    loading: "Загрузка",
+    dashboard: "Панель приборов",
+  },
+  dashboard: {
+    invoice: {
+      createButton: "Создать случайный счет-фактуру",
     },
-    dashboard: {
-        invoice: {
-            createButton: "Создать случайный счет-фактуру"
-        }
-    }
-}
+  },
+};
 
-export default ru
\ No newline at end of file
+export default ru;
diff --git a/src/bootstrap/i18n/i18n-provider.tsx b/src/bootstrap/i18n/i18n-provider.tsx
index d15b347..c3272d8 100644
--- a/src/bootstrap/i18n/i18n-provider.tsx
+++ b/src/bootstrap/i18n/i18n-provider.tsx
@@ -1,13 +1,18 @@
-"use client"
-import { I18nextProvider } from "react-i18next"
+"use client";
+
+import { I18nextProvider } from "react-i18next";
 import { initI18next } from "@/bootstrap/i18n/i18n";
 import { createInstance, Resource } from "i18next";
 import { PropsWithChildren } from "react";
 
-export default function TranslationsProvider({children, lng, resources}: PropsWithChildren & {lng: string; resources: Resource}) {
-    const i18n = createInstance()
+export default function TranslationsProvider({
+  children,
+  lng,
+  resources,
+}: PropsWithChildren & { lng: string; resources: Resource }) {
+  const i18n = createInstance();
 
-    initI18next({lng, i18n, resources})
+  initI18next({ lng, i18n, resources });
 
-    return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
-}
\ No newline at end of file
+  return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
+}
diff --git a/src/bootstrap/i18n/i18n.ts b/src/bootstrap/i18n/i18n.ts
index edd45d5..8b348f2 100644
--- a/src/bootstrap/i18n/i18n.ts
+++ b/src/bootstrap/i18n/i18n.ts
@@ -1,34 +1,51 @@
-import { getOptions, languages } from '@/bootstrap/i18n/settings'
-import { createInstance, i18n, Resource } from 'i18next'
-import resourcesToBackend from 'i18next-resources-to-backend'
-import { initReactI18next } from 'react-i18next/initReactI18next'
+import { getOptions, languages } from "@/bootstrap/i18n/settings";
+import { createInstance, i18n, Resource } from "i18next";
+import resourcesToBackend from "i18next-resources-to-backend";
+import { initReactI18next } from "react-i18next/initReactI18next";
 
-export const initI18next = async (params: {lng: string, i18n?: i18n,  resources?: Resource, ns?: string}) => {
-    const { lng, i18n, ns, resources } = params
-  const i18nInstance = i18n ? i18n : createInstance()
+export const initI18next = async (params: {
+  lng: string;
+  i18n?: i18n;
+  resources?: Resource;
+  ns?: string;
+}) => {
+  const { lng, i18n, ns, resources } = params;
+  const i18nInstance = i18n || createInstance();
   await i18nInstance
     .use(initReactI18next)
-    .use(resourcesToBackend((language: string) => import(`./dictionaries/${language}.ts`)))
+    .use(
+      resourcesToBackend(
+        (language: string) => import(`./dictionaries/${language}.ts`),
+      ),
+    )
     .init({
-        ...getOptions(lng, ns),
-        resources,
-        preload: resources ? [] : languages 
-    },)
+      ...getOptions(lng, ns),
+      resources,
+      preload: resources ? [] : languages,
+    });
 
-    await i18nInstance.init()
- 
-    return {
-        i18n: i18nInstance,
-        resources: i18nInstance.services.resourceStore.data,
-        t: i18nInstance.t
-    } 
-}
+  await i18nInstance.init();
 
-export async function getServerTranslation(lng: string, ns?: string, options: {keyPrefix?: string} = {}) {
-  const i18nextInstance = (await initI18next({lng, ns})).i18n
-  
   return {
-    t: i18nextInstance.getFixedT(lng, Array.isArray(ns) ? ns[0] : ns, options?.keyPrefix),
-    i18n: i18nextInstance
-  }
-}
\ No newline at end of file
+    i18n: i18nInstance,
+    resources: i18nInstance.services.resourceStore.data,
+    t: i18nInstance.t,
+  };
+};
+
+export async function getServerTranslation(
+  lng: string,
+  ns?: string,
+  options: { keyPrefix?: string } = {},
+) {
+  const i18nextInstance = (await initI18next({ lng, ns })).i18n;
+
+  return {
+    t: i18nextInstance.getFixedT(
+      lng,
+      Array.isArray(ns) ? ns[0] : ns,
+      options?.keyPrefix,
+    ),
+    i18n: i18nextInstance,
+  };
+}
diff --git a/src/bootstrap/i18n/settings.ts b/src/bootstrap/i18n/settings.ts
index 7e3030b..384dfd0 100644
--- a/src/bootstrap/i18n/settings.ts
+++ b/src/bootstrap/i18n/settings.ts
@@ -1,9 +1,9 @@
-export const fallbackLng = 'en'
-export const languages = [fallbackLng, 'ru']
-export const defaultNS = 'translation'
-export const cookieName = 'i18next'
+export const fallbackLng = "en";
+export const languages = [fallbackLng, "ru"];
+export const defaultNS = "translation";
+export const cookieName = "i18next";
 
-export function getOptions (lng = fallbackLng, ns = defaultNS) {
+export function getOptions(lng = fallbackLng, ns = defaultNS) {
   return {
     // debug: true,
     supportedLngs: languages,
@@ -11,6 +11,6 @@ export function getOptions (lng = fallbackLng, ns = defaultNS) {
     lng,
     fallbackNS: defaultNS,
     defaultNS,
-    ns
-  }
-}
\ No newline at end of file
+    ns,
+  };
+}
diff --git a/src/feature/common/data/api-task.ts b/src/feature/common/data/api-task.ts
index f234898..2f85177 100644
--- a/src/feature/common/data/api-task.ts
+++ b/src/feature/common/data/api-task.ts
@@ -2,7 +2,10 @@ import { Either } from "fp-ts/lib/Either";
 import { TaskEither } from "fp-ts/lib/TaskEither";
 import BaseFailure from "@/feature/common/failures/base-failure";
 
-type ApiTask<ResponseType> = TaskEither<BaseFailure, ResponseType>;
-export type ApiEither<ResponseType> = Either<BaseFailure, ResponseType>;
+type ApiTask<ResponseType> = TaskEither<BaseFailure<unknown>, ResponseType>;
+export type ApiEither<ResponseType> = Either<
+  BaseFailure<unknown>,
+  ResponseType
+>;
 
 export default ApiTask;
diff --git a/src/feature/common/failures/base-failure.ts b/src/feature/common/failures/base-failure.ts
index 43e6117..2719351 100644
--- a/src/feature/common/failures/base-failure.ts
+++ b/src/feature/common/failures/base-failure.ts
@@ -15,12 +15,12 @@ export default abstract class BaseFailure<META_DATA> {
   message = this.BASE_FAILURE_MESSAGE;
 
   /* -------------------------------------------------------------------------- */
-  metadata: META_DATA | undefined; 
+  metadata: META_DATA | undefined;
 
   /* -------------------------------------------------------------------------- */
   constructor(key: string, metadata?: META_DATA) {
     this.message = makeFailureMessage(this.message, key);
-    this.metadata = metadata ?? undefined
+    this.metadata = metadata ?? undefined;
   }
   /* -------------------------------------------------------------------------- */
 }
diff --git a/src/feature/common/failures/dev/arguments-failure.ts b/src/feature/common/failures/dev/arguments-failure.ts
index 11d01c2..6e0d266 100644
--- a/src/feature/common/failures/dev/arguments-failure.ts
+++ b/src/feature/common/failures/dev/arguments-failure.ts
@@ -3,7 +3,9 @@ import BaseDevFailure from "@/feature/common/failures/dev/base-dev-failure";
 /**
  * Failure for needed arguments in a method but sent wrong one
  */
-export default class ArgumentsFailure<META_DATA> extends BaseDevFailure<META_DATA> {
+export default class ArgumentsFailure<
+  META_DATA,
+> extends BaseDevFailure<META_DATA> {
   /* ------------------------------- Constructor ------------------------------ */
   constructor(metadata?: META_DATA) {
     super("arguments", metadata);
diff --git a/src/feature/common/failures/dev/base-dev-failure.ts b/src/feature/common/failures/dev/base-dev-failure.ts
index ad404d1..94bea0c 100644
--- a/src/feature/common/failures/dev/base-dev-failure.ts
+++ b/src/feature/common/failures/dev/base-dev-failure.ts
@@ -1,3 +1,5 @@
 import BaseFailure from "@/feature/common/failures/base-failure";
 
-export default abstract class BaseDevFailure<META_DATA> extends BaseFailure<META_DATA> {}
+export default abstract class BaseDevFailure<
+  META_DATA,
+> extends BaseFailure<META_DATA> {}
diff --git a/src/feature/common/failures/dev/dependency-failure.ts b/src/feature/common/failures/dev/dependency-failure.ts
index 6f16f8f..5b81b13 100644
--- a/src/feature/common/failures/dev/dependency-failure.ts
+++ b/src/feature/common/failures/dev/dependency-failure.ts
@@ -3,7 +3,9 @@ import BaseDevFailure from "@/feature/common/failures/dev/base-dev-failure";
 /**
  * This is a failure of not having specific dependency
  */
-export default class DependencyFailure<META_DATA> extends BaseDevFailure<META_DATA> {
+export default class DependencyFailure<
+  META_DATA,
+> extends BaseDevFailure<META_DATA> {
   constructor(metadata: META_DATA) {
     super("DependencyFailure", metadata);
   }
diff --git a/src/feature/common/failures/params-failure.ts b/src/feature/common/failures/params-failure.ts
index df50d8c..67f8cb9 100644
--- a/src/feature/common/failures/params-failure.ts
+++ b/src/feature/common/failures/params-failure.ts
@@ -1,7 +1,7 @@
 import BaseFailure from "./base-failure";
 
 /**
- * Failure for params failure 
+ * Failure for params failure
  */
 export default class ParamsFailure<META_DATA> extends BaseFailure<META_DATA> {
   /* ------------------------------- Constructor ------------------------------ */
diff --git a/src/feature/common/feature-helpers.ts b/src/feature/common/feature-helpers.ts
index 4269137..cb37dd2 100644
--- a/src/feature/common/feature-helpers.ts
+++ b/src/feature/common/feature-helpers.ts
@@ -1,6 +1,5 @@
-export const formatCurrency = (amount: number) => {
-  return (amount / 100).toLocaleString('en-US', {
-    style: 'currency',
-    currency: 'USD',
+export const formatCurrency = (amount: number) =>
+  (amount / 100).toLocaleString("en-US", {
+    style: "currency",
+    currency: "USD",
   });
-};
\ No newline at end of file
diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts
index cec83f7..71ddbfa 100644
--- a/src/feature/common/server-di.ts
+++ b/src/feature/common/server-di.ts
@@ -10,21 +10,21 @@ import getSummaryInfoDi from "@/feature/core/summary-info/data/module/summary-in
 import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
 import getRevenueDi from "@/feature/core/revenue/data/module/revenue-di";
 
-const memoizedDis: Record<string, DependencyContainer> = {}
+const memoizedDis: Record<string, DependencyContainer> = {};
 
 export default function serverDi(module: string): DependencyContainer {
-    if (memoizedDis[module]) return memoizedDis[module]
-    const getDi = {
-        [customerKey]: getCustomerDi,
-        [customerInvoiceModuleKey]: getCustomerInvoiceDi,
-        [invoiceModuleKey]: getInvoiceDi,
-        [summaryInfoModuleKey]: getSummaryInfoDi,
-        [revenueModuleKey]: getRevenueDi,
-    }[module]
+  if (memoizedDis[module]) return memoizedDis[module];
+  const getDi = {
+    [customerKey]: getCustomerDi,
+    [customerInvoiceModuleKey]: getCustomerInvoiceDi,
+    [invoiceModuleKey]: getInvoiceDi,
+    [summaryInfoModuleKey]: getSummaryInfoDi,
+    [revenueModuleKey]: getRevenueDi,
+  }[module];
 
-    if (!getDi) throw new Error("Server Di didn't found for module: " + module)
-    
-    const di = getDi()
-    memoizedDis[module] = di 
-    return di
-}
\ No newline at end of file
+  if (!getDi) throw new Error(`Server Di didn't found for module: ${module}`);
+
+  const di = getDi();
+  memoizedDis[module] = di;
+  return di;
+}
diff --git a/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts b/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
index 5364a92..e470b22 100644
--- a/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
+++ b/src/feature/core/customer-invoice/data/module/customer-invoice-di.ts
@@ -4,8 +4,8 @@ import { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i
 import { DependencyContainer } from "tsyringe";
 
 export default function getCustomerInvoiceDi(): DependencyContainer {
-    const customerInvoiceDi = di.createChildContainer()
+  const customerInvoiceDi = di.createChildContainer();
 
-    customerInvoiceDi.register(customerInvoiceRepoKey, CustomerInvoiceDbRepo)
-    return customerInvoiceDi
-}
\ No newline at end of file
+  customerInvoiceDi.register(customerInvoiceRepoKey, CustomerInvoiceDbRepo);
+  return customerInvoiceDi;
+}
diff --git a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
index bc35087..243b8e1 100644
--- a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
+++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts
@@ -15,39 +15,44 @@ type customerInvoiceDbResponse = {
   image_url: string;
   email: string;
   amount: string;
-}
+};
 
 export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
-    fetchList(): ApiTask<CustomerInvoice[]> {
-        return pipe(
-            tryCatch(
-                async () => {
-                    const response = await sql`
+  fetchList(): ApiTask<CustomerInvoice[]> {
+    return pipe(
+      tryCatch(
+        async () => {
+          const response = (await sql`
                     SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
                     FROM invoices
                     JOIN customers ON invoices.customer_id = customers.id
                     ORDER BY invoices.date DESC
-                    LIMIT 20 ` as postgres.RowList<customerInvoiceDbResponse[]>;
-                    
-                    return  this.customerInvoicesDto(response)
-                },
-                (l) => failureOr(l, new NetworkFailure())
-            )
-        )
-    }
+                    LIMIT 20 `) as postgres.RowList<
+            customerInvoiceDbResponse[]
+          >;
 
-    private customerInvoicesDto(dbCustomers: customerInvoiceDbResponse[]): CustomerInvoice[] {
-        return  dbCustomers.map((customer) => this.customerInvoiceDto(customer));
-    }
+          return this.customerInvoicesDto(response);
+        },
+        (l) => failureOr(l, new NetworkFailure()),
+      ),
+    );
+  }
 
-    private customerInvoiceDto(dbCustomer: customerInvoiceDbResponse): CustomerInvoice {
-        return new CustomerInvoice({
-            id: dbCustomer.id,
-            customerName: dbCustomer.name,
-            customerEmail: dbCustomer.email,
-            customerImageUrl: dbCustomer.image_url,
-            invoicesAmount: formatCurrency(+dbCustomer.amount),
-        })
-    }
+  private customerInvoicesDto(
+    dbCustomers: customerInvoiceDbResponse[],
+  ): CustomerInvoice[] {
+    return dbCustomers.map((customer) => this.customerInvoiceDto(customer));
+  }
 
-}
\ No newline at end of file
+  private customerInvoiceDto(
+    dbCustomer: customerInvoiceDbResponse,
+  ): CustomerInvoice {
+    return new CustomerInvoice({
+      id: dbCustomer.id,
+      customerName: dbCustomer.name,
+      customerEmail: dbCustomer.email,
+      customerImageUrl: dbCustomer.image_url,
+      invoicesAmount: formatCurrency(+dbCustomer.amount),
+    });
+  }
+}
diff --git a/src/feature/core/customer-invoice/domain/entity/customer-invoice.ts b/src/feature/core/customer-invoice/domain/entity/customer-invoice.ts
index 783654c..9619393 100644
--- a/src/feature/core/customer-invoice/domain/entity/customer-invoice.ts
+++ b/src/feature/core/customer-invoice/domain/entity/customer-invoice.ts
@@ -1,21 +1,25 @@
 export default class CustomerInvoice {
-    id: string;
-    customerName: string;
-    customerImageUrl: string;
-    customerEmail: string;
-    invoicesAmount: string;
+  id: string;
 
-    constructor({
-        id,
-        customerEmail,
-        customerImageUrl,
-        customerName,
-        invoicesAmount
-    }: CustomerInvoice) {
-        this.id = id;
-        this.customerEmail = customerEmail
-        this.customerImageUrl = customerImageUrl
-        this.customerName = customerName
-        this.invoicesAmount = invoicesAmount
-    }
-}
\ No newline at end of file
+  customerName: string;
+
+  customerImageUrl: string;
+
+  customerEmail: string;
+
+  invoicesAmount: string;
+
+  constructor({
+    id,
+    customerEmail,
+    customerImageUrl,
+    customerName,
+    invoicesAmount,
+  }: CustomerInvoice) {
+    this.id = id;
+    this.customerEmail = customerEmail;
+    this.customerImageUrl = customerImageUrl;
+    this.customerName = customerName;
+    this.invoicesAmount = invoicesAmount;
+  }
+}
diff --git a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
index ab4b486..17a7f4d 100644
--- a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
+++ b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts
@@ -1,8 +1,8 @@
-import ApiTask from "@/feature/common/data/api-task"
-import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice"
+import ApiTask from "@/feature/common/data/api-task";
+import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
 
 export default interface CustomerInvoiceRepo {
-    fetchList(): ApiTask<CustomerInvoice[]>
+  fetchList(): ApiTask<CustomerInvoice[]>;
 }
 
-export const customerInvoiceRepoKey = "customerInvoiceRepoKey"
\ No newline at end of file
+export const customerInvoiceRepoKey = "customerInvoiceRepoKey";
diff --git a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
index 5d2df55..a2bd48e 100644
--- a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
+++ b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
@@ -1,12 +1,18 @@
 import { ApiEither } from "@/feature/common/data/api-task";
 import serverDi from "@/feature/common/server-di";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
-import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
+import CustomerInvoiceRepo, {
+  customerInvoiceRepoKey,
+} from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
 import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
 import { connection } from "next/server";
 
-export default async function fetchCustomerInvoicesUsecase(): Promise<ApiEither<CustomerInvoice[]>> {
-    connection()
-    const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey)
-    return repo.fetchList()()
-}
\ No newline at end of file
+export default async function fetchCustomerInvoicesUsecase(): Promise<
+  ApiEither<CustomerInvoice[]>
+> {
+  connection();
+  const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(
+    customerInvoiceRepoKey,
+  );
+  return repo.fetchList()();
+}
diff --git a/src/feature/core/customer-invoice/invoice-module-key.ts b/src/feature/core/customer-invoice/invoice-module-key.ts
index 6ab1512..3094d82 100644
--- a/src/feature/core/customer-invoice/invoice-module-key.ts
+++ b/src/feature/core/customer-invoice/invoice-module-key.ts
@@ -1 +1 @@
-export const customerInvoiceModuleKey = "customerInvoiceModuleKey"
\ No newline at end of file
+export const customerInvoiceModuleKey = "customerInvoiceModuleKey";
diff --git a/src/feature/core/customer/customer-key.ts b/src/feature/core/customer/customer-key.ts
index 9ae5f45..d3e55c2 100644
--- a/src/feature/core/customer/customer-key.ts
+++ b/src/feature/core/customer/customer-key.ts
@@ -1 +1 @@
-export const customerKey = "customerKey"
\ No newline at end of file
+export const customerKey = "customerKey";
diff --git a/src/feature/core/customer/data/module/customer-di.ts b/src/feature/core/customer/data/module/customer-di.ts
index c3806ee..99afe05 100644
--- a/src/feature/core/customer/data/module/customer-di.ts
+++ b/src/feature/core/customer/data/module/customer-di.ts
@@ -4,8 +4,8 @@ import { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-
 import { DependencyContainer } from "tsyringe";
 
 export default function getCustomerDi(): DependencyContainer {
-    const customerDi = di.createChildContainer()
+  const customerDi = di.createChildContainer();
 
-    customerDi.register(customerRepoKey, CustomerDbRepo)
-    return customerDi
-}
\ No newline at end of file
+  customerDi.register(customerRepoKey, CustomerDbRepo);
+  return customerDi;
+}
diff --git a/src/feature/core/customer/data/repo/customer-db-repo.ts b/src/feature/core/customer/data/repo/customer-db-repo.ts
index 8c6fba4..5d08bc7 100644
--- a/src/feature/core/customer/data/repo/customer-db-repo.ts
+++ b/src/feature/core/customer/data/repo/customer-db-repo.ts
@@ -1,7 +1,12 @@
 import { sql } from "@/bootstrap/boundaries/db/db";
+import ApiTask from "@/feature/common/data/api-task";
+import { failureOr } from "@/feature/common/failures/failure-helpers";
+import NetworkFailure from "@/feature/common/failures/network-failure";
 import { formatCurrency } from "@/feature/common/feature-helpers";
 import Customer from "@/feature/core/customer/domain/entity/customer";
 import CustomerRepo from "@/feature/core/customer/domain/i-repo/customer-repo";
+import { pipe } from "fp-ts/lib/function";
+import { map, tryCatch } from "fp-ts/lib/TaskEither";
 import postgres from "postgres";
 
 type customerDbResponse = {
@@ -12,57 +17,60 @@ type customerDbResponse = {
   total_invoices: string;
   total_pending: string;
   total_paid: string;
-}
+};
 
 export default class CustomerDbRepo implements CustomerRepo {
-    async fetchList(query: string): Promise<Customer[]> {
-        try {
-            const data = await sql`
-                SELECT
-                customers.id,
-                customers.name,
-                customers.email,
-                customers.image_url,
-                COUNT(invoices.id) AS total_invoices,
-                SUM(CASE WHEN invoices.status = 'pending' THEN invoices.amount ELSE 0 END) AS total_pending,
-                SUM(CASE WHEN invoices.status = 'paid' THEN invoices.amount ELSE 0 END) AS total_paid
-                FROM customers
-                LEFT JOIN invoices ON customers.id = invoices.customer_id
-                WHERE
-                customers.name ILIKE ${`%${query}%`} OR
-                customers.email ILIKE ${`%${query}%`}
-                GROUP BY customers.id, customers.name, customers.email, customers.image_url
-                ORDER BY customers.name ASC
-            ` as postgres.RowList<customerDbResponse[]>;
+  fetchList(query: string): ApiTask<Customer[]> {
+    return pipe(
+      tryCatch(
+        async () => {
+          const data = (await sql`
+                    SELECT
+                    customers.id,
+                    customers.name,
+                    customers.email,
+                    customers.image_url,
+                    COUNT(invoices.id) AS total_invoices,
+                    SUM(CASE WHEN invoices.status = 'pending' THEN invoices.amount ELSE 0 END) AS total_pending,
+                    SUM(CASE WHEN invoices.status = 'paid' THEN invoices.amount ELSE 0 END) AS total_paid
+                    FROM customers
+                    LEFT JOIN invoices ON customers.id = invoices.customer_id
+                    WHERE
+                    customers.name ILIKE ${`%${query}%`} OR
+                    customers.email ILIKE ${`%${query}%`}
+                    GROUP BY customers.id, customers.name, customers.email, customers.image_url
+                    ORDER BY customers.name ASC
+                `) as postgres.RowList<customerDbResponse[]>;
 
+          return data;
+        },
+        (l) => failureOr(l, new NetworkFailure(l as Error)),
+      ),
+      map(this.customersDto.bind(this)),
+    ) as ApiTask<Customer[]>;
+  }
 
-            return this.customersDto(data);
-        } catch (err) {
-            console.error('Database Error:', err);
-            throw new Error('Failed to fetch customer table.');
-        }
-    }
+  async fetchCustomersAmount(): Promise<number> {
+    const data =
+      (await sql`SELECT COUNT(*) FROM customers`) as postgres.RowList<
+        unknown[]
+      >;
+    return Number(data.count ?? "0");
+  }
 
-    async fetchCustomersAmount(): Promise<number> {
-        const data = await sql`SELECT COUNT(*) FROM customers`as postgres.RowList<unknown[]>;
-        return Number(data.count ?? '0');
-    }
+  private customersDto(dbCustomers: customerDbResponse[]): Customer[] {
+    return dbCustomers.map((customer) => this.customerDto(customer));
+  }
 
-
-    private customersDto(dbCustomers: customerDbResponse[]): Customer[] {
-        return  dbCustomers.map((customer) => this.customerDto(customer));
-    }
-
-    private customerDto(dbCustomer: customerDbResponse): Customer {
-        return new Customer({
-            id: dbCustomer.id,
-            name: dbCustomer.name,
-            email: dbCustomer.email,
-            imageUrl: dbCustomer.image_url,
-            totalInvoices: dbCustomer.total_invoices,
-            totalPending: formatCurrency(Number(dbCustomer.total_pending)),
-            totalPaid: formatCurrency(Number(dbCustomer.total_paid)),
-        })
-    }
-
-}
\ No newline at end of file
+  private customerDto(dbCustomer: customerDbResponse): Customer {
+    return new Customer({
+      id: dbCustomer.id,
+      name: dbCustomer.name,
+      email: dbCustomer.email,
+      imageUrl: dbCustomer.image_url,
+      totalInvoices: dbCustomer.total_invoices,
+      totalPending: formatCurrency(Number(dbCustomer.total_pending)),
+      totalPaid: formatCurrency(Number(dbCustomer.total_paid)),
+    });
+  }
+}
diff --git a/src/feature/core/customer/domain/entity/customer.ts b/src/feature/core/customer/domain/entity/customer.ts
index 6f32ec3..ced9522 100644
--- a/src/feature/core/customer/domain/entity/customer.ts
+++ b/src/feature/core/customer/domain/entity/customer.ts
@@ -1,27 +1,33 @@
 export default class Customer {
-    id: string;
-    name: string;
-    email: string;
-    imageUrl: string;
-    totalInvoices: string;
-    totalPending: string;
-    totalPaid: string;
+  id: string;
 
-    constructor({
-        id,
-        email,
-        imageUrl,
-        name,
-        totalInvoices,
-        totalPaid,
-        totalPending
-    }: Customer) {
-        this.id = id;
-        this.name = name;
-        this.email = email;
-        this.imageUrl = imageUrl;
-        this.totalInvoices = totalInvoices;
-        this.totalPaid = totalPaid;
-        this.totalPending = totalPending;
-    }
-}
\ No newline at end of file
+  name: string;
+
+  email: string;
+
+  imageUrl: string;
+
+  totalInvoices: string;
+
+  totalPending: string;
+
+  totalPaid: string;
+
+  constructor({
+    id,
+    email,
+    imageUrl,
+    name,
+    totalInvoices,
+    totalPaid,
+    totalPending,
+  }: Customer) {
+    this.id = id;
+    this.name = name;
+    this.email = email;
+    this.imageUrl = imageUrl;
+    this.totalInvoices = totalInvoices;
+    this.totalPaid = totalPaid;
+    this.totalPending = totalPending;
+  }
+}
diff --git a/src/feature/core/customer/domain/i-repo/customer-repo.ts b/src/feature/core/customer/domain/i-repo/customer-repo.ts
index 86b890e..f631be0 100644
--- a/src/feature/core/customer/domain/i-repo/customer-repo.ts
+++ b/src/feature/core/customer/domain/i-repo/customer-repo.ts
@@ -1,9 +1,9 @@
-import ApiTask from "@/feature/common/data/api-task"
-import Customer from "@/feature/core/customer/domain/entity/customer"
+import ApiTask from "@/feature/common/data/api-task";
+import Customer from "@/feature/core/customer/domain/entity/customer";
 
 export default interface CustomerRepo {
-    fetchList(query: string): ApiTask<Customer[]>
-    fetchCustomersAmount(): Promise<number>
+  fetchList(query: string): ApiTask<Customer[]>;
+  fetchCustomersAmount(): Promise<number>;
 }
 
-export const customerRepoKey = "customerRepoKey"
\ No newline at end of file
+export const customerRepoKey = "customerRepoKey";
diff --git a/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
index 71cd13d..2f5f620 100644
--- a/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
@@ -1,8 +1,10 @@
 import serverDi from "@/feature/common/server-di";
 import { customerKey } from "@/feature/core/customer/customer-key";
-import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
+import CustomerRepo, {
+  customerRepoKey,
+} from "@/feature/core/customer/domain/i-repo/customer-repo";
 
 export default async function fetchCustomersAmountUsecase(): Promise<number> {
-    const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
-    return repo.fetchCustomersAmount()
-}
\ No newline at end of file
+  const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey);
+  return repo.fetchCustomersAmount();
+}
diff --git a/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
index c83a252..716b190 100644
--- a/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
@@ -1,14 +1,19 @@
-"use server"
+"use server";
 
+import { ApiEither } from "@/feature/common/data/api-task";
 import serverDi from "@/feature/common/server-di";
 import { customerKey } from "@/feature/core/customer/customer-key";
 import Customer from "@/feature/core/customer/domain/entity/customer";
-import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
+import CustomerRepo, {
+  customerRepoKey,
+} from "@/feature/core/customer/domain/i-repo/customer-repo";
 import { connection } from "next/server";
 
-export default async function fetchCustomersUsecase(query: string): Promise<Customer[]> {
-    connection()
-    const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
+export default function fetchCustomersUsecase(
+  query: string,
+): Promise<ApiEither<Customer[]>> {
+  connection();
+  const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey);
 
-    return repo.fetchList(query)
-}
\ No newline at end of file
+  return repo.fetchList(query)();
+}
diff --git a/src/feature/core/invoice/data/module/invoice-di.ts b/src/feature/core/invoice/data/module/invoice-di.ts
index 1434954..e42ae4b 100644
--- a/src/feature/core/invoice/data/module/invoice-di.ts
+++ b/src/feature/core/invoice/data/module/invoice-di.ts
@@ -4,8 +4,8 @@ import { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-rep
 import { DependencyContainer } from "tsyringe";
 
 export default function getInvoiceDi(): DependencyContainer {
-    const invoiceDi = di.createChildContainer()
+  const invoiceDi = di.createChildContainer();
 
-    invoiceDi.register(invoiceRepoKey, invoiceDbRepo)
-    return invoiceDi
-}
\ No newline at end of file
+  invoiceDi.register(invoiceRepoKey, invoiceDbRepo);
+  return invoiceDi;
+}
diff --git a/src/feature/core/invoice/data/repo/invoice-db-repo.ts b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
index 416c060..9c7603a 100644
--- a/src/feature/core/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
@@ -10,57 +10,60 @@ import { pipe } from "fp-ts/lib/function";
 import { tryCatch } from "fp-ts/lib/TaskEither";
 import postgres from "postgres";
 
-type InvoiceSummaryDbResponse = {paid: string, pending: string}
+type InvoiceSummaryDbResponse = { paid: string; pending: string };
 export default class InvoiceDbRepo implements InvoiceRepo {
-    async fetchAllInvoicesAmount(): Promise<number> {
-        const data = await sql`SELECT COUNT(*) FROM invoices` as postgres.RowList<unknown[]>;
-        
-        return data.count ?? 0
-    }
+  async fetchAllInvoicesAmount(): Promise<number> {
+    const data = (await sql`SELECT COUNT(*) FROM invoices`) as postgres.RowList<
+      unknown[]
+    >;
 
-    createInvoice(params: InvoiceParam): ApiTask<string> {
-        return pipe(
-            tryCatch(
-                async () => {
-                    const firstCustomerIdDb = await sql`SELECT 
+    return data.count ?? 0;
+  }
+
+  createInvoice(params: InvoiceParam): ApiTask<string> {
+    return pipe(
+      tryCatch(
+        async () => {
+          const firstCustomerIdDb = await sql`SELECT 
                         id FROM customers 
                         ORDER BY id DESC 
                         LIMIT 1
-                    `
-                    const customerId = firstCustomerIdDb.at(0)?.id
-                    if (!customerId) throw new Error("There is no customer")
-                    
-                    const { amount, status } = params;
-                    const amountInCents = amount * 100;
-                    const date = new Date().toISOString().split('T')[0];
-            
-                    // Insert data into the database
-                    const result = await sql`
+                    `;
+          const customerId = firstCustomerIdDb.at(0)?.id;
+          if (!customerId) throw new Error("There is no customer");
+
+          const { amount, status } = params;
+          const amountInCents = amount * 100;
+          const date = new Date().toISOString().split("T")[0];
+
+          // Insert data into the database
+          const result = await sql`
                         INSERT INTO invoices (customer_id, amount, status, date)
                         VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
                         RETURNING id
                     `;
-                    return result.at(0)?.id ?? ""
-                },
-                (l) => failureOr(l, new NetworkFailure(l as Error))
-            ),
-        )
-    }
+          return result.at(0)?.id ?? "";
+        },
+        (l) => failureOr(l, new NetworkFailure(l as Error)),
+      ),
+    );
+  }
 
-    async fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
-        const invoiceStatusPromise = await sql`SELECT
+  async fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
+    const invoiceStatusPromise = (await sql`SELECT
             SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
             SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
-            FROM invoices` as postgres.RowList<InvoiceSummaryDbResponse[]>;
-        
-        return this.invoiceSummaryDto(invoiceStatusPromise.at(0))
-        
-    }
+            FROM invoices`) as postgres.RowList<InvoiceSummaryDbResponse[]>;
 
-    private invoiceSummaryDto(dbResponse?: InvoiceSummaryDbResponse): InvoiceStatusSummary {
-        return new InvoiceStatusSummary({
-            paid: formatCurrency(Number(dbResponse?.paid ?? '0')),
-            pending: formatCurrency(Number(dbResponse?.pending ?? '0'))
-        })
-    }
-}
\ No newline at end of file
+    return this.invoiceSummaryDto(invoiceStatusPromise.at(0));
+  }
+
+  private invoiceSummaryDto(
+    dbResponse?: InvoiceSummaryDbResponse,
+  ): InvoiceStatusSummary {
+    return new InvoiceStatusSummary({
+      paid: formatCurrency(Number(dbResponse?.paid ?? "0")),
+      pending: formatCurrency(Number(dbResponse?.pending ?? "0")),
+    });
+  }
+}
diff --git a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
index ca5096d..049fa98 100644
--- a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
+++ b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
@@ -1,11 +1,11 @@
-import ApiTask from "@/feature/common/data/api-task"
-import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param"
-import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status"
+import ApiTask from "@/feature/common/data/api-task";
+import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
+import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
 
 export default interface InvoiceRepo {
-    fetchAllInvoicesAmount(): Promise<number>
-    fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary>
-    createInvoice(params: InvoiceParam): ApiTask<string>
+  fetchAllInvoicesAmount(): Promise<number>;
+  fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary>;
+  createInvoice(params: InvoiceParam): ApiTask<string>;
 }
 
-export const invoiceRepoKey = "invoiceRepoKey"
\ No newline at end of file
+export const invoiceRepoKey = "invoiceRepoKey";
diff --git a/src/feature/core/invoice/domain/param/invoice-param.ts b/src/feature/core/invoice/domain/param/invoice-param.ts
index 1dce62a..fadf7da 100644
--- a/src/feature/core/invoice/domain/param/invoice-param.ts
+++ b/src/feature/core/invoice/domain/param/invoice-param.ts
@@ -1,10 +1,12 @@
 import { z } from "zod";
 
 export const invoiceSchema = z.object({
-  amount: z.coerce.number().gt(0, { message: 'Please enter an amount greater than $0.' }),
-  status: z.enum(['pending', 'paid'], {
-    invalid_type_error: 'Please select an invoice status.',
+  amount: z.coerce
+    .number()
+    .gt(0, { message: "Please enter an amount greater than $0." }),
+  status: z.enum(["pending", "paid"], {
+    invalid_type_error: "Please select an invoice status.",
   }),
 });
 
-export type InvoiceParam = z.infer<typeof invoiceSchema>
\ No newline at end of file
+export type InvoiceParam = z.infer<typeof invoiceSchema>;
diff --git a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
index 9cfc63d..7c2f136 100644
--- a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
+++ b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
@@ -1,24 +1,32 @@
-"use server"
+"use server";
+
 import { ApiEither } from "@/feature/common/data/api-task";
 import ParamsFailure from "@/feature/common/failures/params-failure";
 import serverDi from "@/feature/common/server-di";
-import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
-import { InvoiceParam, invoiceSchema } from "@/feature/core/invoice/domain/param/invoice-param";
+import InvoiceRepo, {
+  invoiceRepoKey,
+} from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import {
+  InvoiceParam,
+  invoiceSchema,
+} from "@/feature/core/invoice/domain/param/invoice-param";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 import { pipe } from "fp-ts/lib/function";
 import { chain, fromNullable, left, map, right } from "fp-ts/lib/TaskEither";
 
-export default async function createInvoiceUsecase(params: InvoiceParam): Promise<ApiEither<string>> {
-    const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
+export default async function createInvoiceUsecase(
+  params: InvoiceParam,
+): Promise<ApiEither<string>> {
+  const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey);
 
-    return pipe(
-        fromNullable(new ParamsFailure())(params),
-        map((params) => invoiceSchema.safeParse(params)),
-        chain((params) => {
-            const isParamsValid = invoiceSchema.safeParse(params)
-            if (!isParamsValid.success) left(new ParamsFailure())
-            return right(params.data as InvoiceParam)
-        }),
-        chain((params) => repo.createInvoice(params))
-    )()
-}
\ No newline at end of file
+  return pipe(
+    fromNullable(new ParamsFailure())(params),
+    map((params) => invoiceSchema.safeParse(params)),
+    chain((params) => {
+      const isParamsValid = invoiceSchema.safeParse(params);
+      if (!isParamsValid.success) left(new ParamsFailure());
+      return right(params.data as InvoiceParam);
+    }),
+    chain((params) => repo.createInvoice(params)),
+  )();
+}
diff --git a/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts b/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
index 48840f4..8b8d04a 100644
--- a/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
+++ b/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
@@ -1,10 +1,13 @@
-"use server"
+"use server";
+
 import serverDi from "@/feature/common/server-di";
-import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import InvoiceRepo, {
+  invoiceRepoKey,
+} from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 
 export default async function fetchAllInvoicesAmountUsecase(): Promise<number> {
-    const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
+  const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey);
 
-    return repo.fetchAllInvoicesAmount()
-}
\ No newline at end of file
+  return repo.fetchAllInvoicesAmount();
+}
diff --git a/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts b/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
index 3bb9a5e..68c96ad 100644
--- a/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
+++ b/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
@@ -1,9 +1,11 @@
 import serverDi from "@/feature/common/server-di";
-import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import InvoiceRepo, {
+  invoiceRepoKey,
+} from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 
 export default async function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
-    const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
-    return repo.fetchInvoicesStatusSummary()
-}
\ No newline at end of file
+  const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey);
+  return repo.fetchInvoicesStatusSummary();
+}
diff --git a/src/feature/core/invoice/domain/value-object/invoice-status.ts b/src/feature/core/invoice/domain/value-object/invoice-status.ts
index b2c393c..39b99c8 100644
--- a/src/feature/core/invoice/domain/value-object/invoice-status.ts
+++ b/src/feature/core/invoice/domain/value-object/invoice-status.ts
@@ -1,12 +1,10 @@
 export default class InvoiceStatusSummary {
-    paid: string;
-    pending: string;
+  paid: string;
 
-    constructor({
-        paid,
-        pending
-    }: InvoiceStatusSummary) {
-        this.paid = paid;
-        this.pending = pending;
-    }
-}
\ No newline at end of file
+  pending: string;
+
+  constructor({ paid, pending }: InvoiceStatusSummary) {
+    this.paid = paid;
+    this.pending = pending;
+  }
+}
diff --git a/src/feature/core/invoice/invoice-module-key.ts b/src/feature/core/invoice/invoice-module-key.ts
index c9cafd9..118ae9a 100644
--- a/src/feature/core/invoice/invoice-module-key.ts
+++ b/src/feature/core/invoice/invoice-module-key.ts
@@ -1 +1 @@
-export const invoiceModuleKey = "invoiceModuleKey"
\ No newline at end of file
+export const invoiceModuleKey = "invoiceModuleKey";
diff --git a/src/feature/core/revenue/data/module/revenue-di.ts b/src/feature/core/revenue/data/module/revenue-di.ts
index 921a2ed..8e1f828 100644
--- a/src/feature/core/revenue/data/module/revenue-di.ts
+++ b/src/feature/core/revenue/data/module/revenue-di.ts
@@ -1,10 +1,10 @@
-import di from "@/bootstrap/di/init-di"
-import RevenueDbRepo from "@/feature/core/revenue/data/repo/revenue-db-repo"
-import { revenueRepoKey } from "@/feature/core/revenue/domain/i-repo/revenue-repo"
+import di from "@/bootstrap/di/init-di";
+import RevenueDbRepo from "@/feature/core/revenue/data/repo/revenue-db-repo";
+import { revenueRepoKey } from "@/feature/core/revenue/domain/i-repo/revenue-repo";
 
 export default function getRevenueDi() {
-    const revenueDi = di.createChildContainer()
+  const revenueDi = di.createChildContainer();
 
-    revenueDi.register(revenueRepoKey, RevenueDbRepo)
-    return revenueDi
-}
\ No newline at end of file
+  revenueDi.register(revenueRepoKey, RevenueDbRepo);
+  return revenueDi;
+}
diff --git a/src/feature/core/revenue/data/repo/revenue-db-repo.ts b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
index d8d771d..2b89341 100644
--- a/src/feature/core/revenue/data/repo/revenue-db-repo.ts
+++ b/src/feature/core/revenue/data/repo/revenue-db-repo.ts
@@ -1,7 +1,6 @@
 import { sql } from "@/bootstrap/boundaries/db/db";
 import Revenue from "@/feature/core/revenue/domain/entity/revenue";
 import RevenueRepo from "@/feature/core/revenue/domain/i-repo/revenue-repo";
-import { connection } from "next/server";
 import postgres from "postgres";
 
 export type RevenueDbResponse = {
@@ -9,33 +8,30 @@ export type RevenueDbResponse = {
   revenue: number;
 };
 export default class RevenueDbRepo implements RevenueRepo {
-    async fetchRevenues(): Promise<Revenue[]> {
-        try {
-            // Artificially delay a response for demo purposes.
-            // Don't do this in production :)
-            await new Promise((resolve) => setTimeout(resolve, 3000));
+  async fetchRevenues(): Promise<Revenue[]> {
+    try {
+      // Artificially delay a response for demo purposes.
+      // Don't do this in production :)
+      await new Promise((resolve) => setTimeout(resolve, 3000));
 
-            const data = await sql`SELECT * FROM revenue` as postgres.RowList<RevenueDbResponse[]>;
+      const data = (await sql`SELECT * FROM revenue`) as postgres.RowList<
+        RevenueDbResponse[]
+      >;
 
-            console.log('Data fetch completed after 3 seconds.');
-
-            return this.revenuesDto(data);
-        } catch (error) {
-            console.error('Database Error:', error);
-            throw new Error('Failed to fetch revenue data.');
-        }
+      return this.revenuesDto(data);
+    } catch {
+      throw new Error("Failed to fetch revenue data.");
     }
+  }
 
+  private revenuesDto(dbResponse: RevenueDbResponse[]): Revenue[] {
+    return dbResponse.map((dbRevenue) => this.revenueDto(dbRevenue));
+  }
 
-    private revenuesDto(dbResponse: RevenueDbResponse[]): Revenue[] {
-        return dbResponse.map((dbRevenue) => this.revenueDto(dbRevenue))
-    }
-    
-    private revenueDto(dbResponse: RevenueDbResponse): Revenue {
-        return new Revenue({
-            month: dbResponse.month,
-            revenue: dbResponse.revenue
-        })
-    }
-    
-}
\ No newline at end of file
+  private revenueDto(dbResponse: RevenueDbResponse): Revenue {
+    return new Revenue({
+      month: dbResponse.month,
+      revenue: dbResponse.revenue,
+    });
+  }
+}
diff --git a/src/feature/core/revenue/domain/entity/revenue.ts b/src/feature/core/revenue/domain/entity/revenue.ts
index 290ca32..11bd79e 100644
--- a/src/feature/core/revenue/domain/entity/revenue.ts
+++ b/src/feature/core/revenue/domain/entity/revenue.ts
@@ -1,14 +1,10 @@
 export default class Revenue {
-    month: string;
-    revenue: number;
+  month: string;
 
-    constructor(
-        {
-            month,
-            revenue
-        }: Revenue
-    ) {
-        this.month = month
-        this.revenue = revenue
-    }
-}
\ No newline at end of file
+  revenue: number;
+
+  constructor({ month, revenue }: Revenue) {
+    this.month = month;
+    this.revenue = revenue;
+  }
+}
diff --git a/src/feature/core/revenue/domain/i-repo/revenue-repo.ts b/src/feature/core/revenue/domain/i-repo/revenue-repo.ts
index be6924b..7b67fd5 100644
--- a/src/feature/core/revenue/domain/i-repo/revenue-repo.ts
+++ b/src/feature/core/revenue/domain/i-repo/revenue-repo.ts
@@ -1,7 +1,7 @@
 import Revenue from "@/feature/core/revenue/domain/entity/revenue";
 
 export default interface RevenueRepo {
-    fetchRevenues(): Promise<Revenue[]>
+  fetchRevenues(): Promise<Revenue[]>;
 }
 
-export const revenueRepoKey = "revenueRepoKey"
\ No newline at end of file
+export const revenueRepoKey = "revenueRepoKey";
diff --git a/src/feature/core/revenue/domain/revenue-module-key.ts b/src/feature/core/revenue/domain/revenue-module-key.ts
index 27150fb..ebad37f 100644
--- a/src/feature/core/revenue/domain/revenue-module-key.ts
+++ b/src/feature/core/revenue/domain/revenue-module-key.ts
@@ -1 +1 @@
-export const revenueModuleKey = "RevenueModuleKey"
\ No newline at end of file
+export const revenueModuleKey = "RevenueModuleKey";
diff --git a/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
index 0f428c5..57b5a5d 100644
--- a/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
+++ b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
@@ -1,9 +1,11 @@
 import serverDi from "@/feature/common/server-di";
 import Revenue from "@/feature/core/revenue/domain/entity/revenue";
-import RevenueRepo, { revenueRepoKey } from "@/feature/core/revenue/domain/i-repo/revenue-repo";
+import RevenueRepo, {
+  revenueRepoKey,
+} from "@/feature/core/revenue/domain/i-repo/revenue-repo";
 import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
 
 export default async function fetchRevenuesUsecase(): Promise<Revenue[]> {
-    const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueRepoKey)
-    return repo.fetchRevenues()
-}
\ No newline at end of file
+  const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueRepoKey);
+  return repo.fetchRevenues();
+}
diff --git a/src/feature/core/summary-info/data/module/summary-info-di.ts b/src/feature/core/summary-info/data/module/summary-info-di.ts
index 9172b3d..78e8bc7 100644
--- a/src/feature/core/summary-info/data/module/summary-info-di.ts
+++ b/src/feature/core/summary-info/data/module/summary-info-di.ts
@@ -1,19 +1,19 @@
-import di from "@/bootstrap/di/init-di"
-import fetchCustomersAmountUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-amount-usecase"
-import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase"
-import fetchInvoicesStatusSummary from "@/feature/core/invoice/domain/usecase/fetch-invoices-status-summary"
+import fetchCustomersAmountUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-amount-usecase";
+import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase";
+import fetchInvoicesStatusSummary from "@/feature/core/invoice/domain/usecase/fetch-invoices-status-summary";
+import di from "@/bootstrap/di/init-di";
 
 export default function getSummaryInfoDi() {
-    const summaryInfoDi = di.createChildContainer()
+  const summaryInfoDi = di.createChildContainer();
 
-    summaryInfoDi.register(fetchAllInvoicesAmountUsecase.name, {
-        useValue: fetchAllInvoicesAmountUsecase
-    })
-    summaryInfoDi.register(fetchCustomersAmountUsecase.name, {
-        useValue: fetchCustomersAmountUsecase
-    })
-    summaryInfoDi.register(fetchInvoicesStatusSummary.name, {
-        useValue: fetchInvoicesStatusSummary 
-    })
-    return summaryInfoDi
-}
\ No newline at end of file
+  summaryInfoDi.register(fetchAllInvoicesAmountUsecase.name, {
+    useValue: fetchAllInvoicesAmountUsecase,
+  });
+  summaryInfoDi.register(fetchCustomersAmountUsecase.name, {
+    useValue: fetchCustomersAmountUsecase,
+  });
+  summaryInfoDi.register(fetchInvoicesStatusSummary.name, {
+    useValue: fetchInvoicesStatusSummary,
+  });
+  return summaryInfoDi;
+}
diff --git a/src/feature/core/summary-info/domain/summary-info-module-key.ts b/src/feature/core/summary-info/domain/summary-info-module-key.ts
index a2e027b..45f5966 100644
--- a/src/feature/core/summary-info/domain/summary-info-module-key.ts
+++ b/src/feature/core/summary-info/domain/summary-info-module-key.ts
@@ -1 +1 @@
-export const summaryInfoModuleKey = "summaryInfoModuleKey"
\ No newline at end of file
+export const summaryInfoModuleKey = "summaryInfoModuleKey";
diff --git a/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts b/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
index e0a6305..33bbec4 100644
--- a/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
+++ b/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
@@ -7,28 +7,33 @@ import SummaryInfo from "@/feature/core/summary-info/domain/value-object/summary
 import { connection } from "next/server";
 
 export default async function fetchSummaryInfoUsecase(): Promise<SummaryInfo> {
-    connection()
+  connection();
 
-    try{
-        const summaryInfoDi = serverDi(summaryInfoModuleKey)
-        const invoicesAmountPromise = summaryInfoDi.resolve<typeof fetchAllInvoicesAmountUsecase>(fetchAllInvoicesAmountUsecase.name)()
-        const customersAmountPromise = summaryInfoDi.resolve<typeof fetchCustomersAmountUsecase>(fetchCustomersAmountUsecase.name)()
-        const invoiceSummaryPomise = summaryInfoDi.resolve<typeof fetchInvoicesStatusSummary>(fetchInvoicesStatusSummary.name)()
+  try {
+    const summaryInfoDi = serverDi(summaryInfoModuleKey);
+    const invoicesAmountPromise = summaryInfoDi.resolve<
+      typeof fetchAllInvoicesAmountUsecase
+    >(fetchAllInvoicesAmountUsecase.name)();
+    const customersAmountPromise = summaryInfoDi.resolve<
+      typeof fetchCustomersAmountUsecase
+    >(fetchCustomersAmountUsecase.name)();
+    const invoiceSummaryPomise = summaryInfoDi.resolve<
+      typeof fetchInvoicesStatusSummary
+    >(fetchInvoicesStatusSummary.name)();
 
-        const [invoicesAmount, customersAmount, invoicesSummary] = await Promise.all([
-            invoicesAmountPromise,
-            customersAmountPromise,
-            invoiceSummaryPomise,
-        ]);
+    const [invoicesAmount, customersAmount, invoicesSummary] =
+      await Promise.all([
+        invoicesAmountPromise,
+        customersAmountPromise,
+        invoiceSummaryPomise,
+      ]);
 
-
-        return new SummaryInfo({
-            invoicesNumber: invoicesAmount,
-            customersNumber: customersAmount,
-            invoicesSummary: invoicesSummary
-        })
-    } catch (error) {
-        console.error('Database Error:', error);
-        throw new Error('Failed to fetch card data.');
-    }
-}
\ No newline at end of file
+    return new SummaryInfo({
+      invoicesNumber: invoicesAmount,
+      customersNumber: customersAmount,
+      invoicesSummary,
+    });
+  } catch {
+    throw new Error("Failed to fetch card data.");
+  }
+}
diff --git a/src/feature/core/summary-info/domain/value-object/summary-info.ts b/src/feature/core/summary-info/domain/value-object/summary-info.ts
index 9c30901..19d4fff 100644
--- a/src/feature/core/summary-info/domain/value-object/summary-info.ts
+++ b/src/feature/core/summary-info/domain/value-object/summary-info.ts
@@ -1,17 +1,19 @@
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
 
 export default class SummaryInfo {
-    customersNumber: number;
-    invoicesNumber: number;
-    invoicesSummary: InvoiceStatusSummary 
+  customersNumber: number;
 
-    constructor({
-        customersNumber,
-        invoicesNumber,
-        invoicesSummary
-    }: SummaryInfo) {
-        this.customersNumber = customersNumber
-        this.invoicesNumber = invoicesNumber
-        this.invoicesSummary = invoicesSummary
-    }
-}
\ No newline at end of file
+  invoicesNumber: number;
+
+  invoicesSummary: InvoiceStatusSummary;
+
+  constructor({
+    customersNumber,
+    invoicesNumber,
+    invoicesSummary,
+  }: SummaryInfo) {
+    this.customersNumber = customersNumber;
+    this.invoicesNumber = invoicesNumber;
+    this.invoicesSummary = invoicesSummary;
+  }
+}
diff --git a/src/middleware.ts b/src/middleware.ts
index 8c6f760..b8ead4c 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -1,36 +1,41 @@
-import { NextRequest, NextResponse } from 'next/server'
-import acceptLanguage from 'accept-language'
-import { cookieName, fallbackLng, languages } from '@/bootstrap/i18n/settings'
+import { NextRequest, NextResponse } from "next/server";
+import acceptLanguage from "accept-language";
+import { cookieName, fallbackLng, languages } from "@/bootstrap/i18n/settings";
 
-acceptLanguage.languages(languages)
+acceptLanguage.languages(languages);
 
 export const config = {
-  matcher: ["/((?!api|static|.*\\..*|_next).*)"]
-}
+  matcher: ["/((?!api|static|.*\\..*|_next).*)"],
+};
 
 export function middleware(req: NextRequest) {
-  let lng
-  if (req.cookies.has(cookieName)) lng = acceptLanguage.get(req?.cookies?.get(cookieName)?.value)
-  if (!lng) lng = acceptLanguage.get(req.headers.get('Accept-Language'))
-  if (!lng) lng = fallbackLng
+  let lng;
+  if (req.cookies.has(cookieName))
+    lng = acceptLanguage.get(req?.cookies?.get(cookieName)?.value);
+  if (!lng) lng = acceptLanguage.get(req.headers.get("Accept-Language"));
+  if (!lng) lng = fallbackLng;
 
   // Redirect if lng in path is not supported
   if (
-    !languages.some(loc => req.nextUrl.pathname.startsWith(`/${loc}`)) &&
-    !req.nextUrl.pathname.startsWith('/_next')
+    !languages.some((loc) => req.nextUrl.pathname.startsWith(`/${loc}`)) &&
+    !req.nextUrl.pathname.startsWith("/_next")
   ) {
-    return NextResponse.redirect(new URL(`/${lng}${req.nextUrl.pathname}`, req.url))
+    return NextResponse.redirect(
+      new URL(`/${lng}${req.nextUrl.pathname}`, req.url),
+    );
   }
 
-  if (req.headers.has('referer')) {
-    const refererUrl = new URL(req?.headers?.get('referer') ?? "")
-    const lngInReferer = languages.find((l) => refererUrl.pathname.startsWith(`/${l}`))
-    const response = NextResponse.next()
+  if (req.headers.has("referer")) {
+    const refererUrl = new URL(req?.headers?.get("referer") ?? "");
+    const lngInReferer = languages.find((l) =>
+      refererUrl.pathname.startsWith(`/${l}`),
+    );
+    const response = NextResponse.next();
     if (lngInReferer) {
-      response.cookies.set(cookieName, lngInReferer)
+      response.cookies.set(cookieName, lngInReferer);
     }
-    return response
+    return response;
   }
 
-  return NextResponse.next()
-}
\ No newline at end of file
+  return NextResponse.next();
+}
diff --git a/src/test/common/fake-factory/customer/customer-fake-factory.ts b/src/test/common/fake-factory/customer/customer-fake-factory.ts
index 319c223..ff2bd38 100644
--- a/src/test/common/fake-factory/customer/customer-fake-factory.ts
+++ b/src/test/common/fake-factory/customer/customer-fake-factory.ts
@@ -2,20 +2,21 @@ import Customer from "@/feature/core/customer/domain/entity/customer";
 import { faker } from "@faker-js/faker";
 
 export default class CustomerFakeFactory {
-    static getFakeCustomer(): Customer {
-        return new Customer({
-            id: faker.string.uuid(),
-            name: faker.person.fullName(),
-            email: faker.internet.email(),
-            imageUrl: faker.image.url(),
-            totalInvoices: faker.number.int().toLocaleString(),
-            totalPaid: faker.finance.amount(),
-            totalPending: faker.number.int().toLocaleString(),
-        })
-    }
+  static getFakeCustomer(): Customer {
+    return new Customer({
+      id: faker.string.uuid(),
+      name: faker.person.fullName(),
+      email: faker.internet.email(),
+      imageUrl: faker.image.url(),
+      totalInvoices: faker.number.int().toLocaleString(),
+      totalPaid: faker.finance.amount(),
+      totalPending: faker.number.int().toLocaleString(),
+    });
+  }
 
-
-    static getFakeCustomerList(length: number = 10): Customer[] {
-        return Array.from({length}).map(() => CustomerFakeFactory.getFakeCustomer()) 
-    }
-}
\ No newline at end of file
+  static getFakeCustomerList(length: number = 10): Customer[] {
+    return Array.from({ length }).map(() =>
+      CustomerFakeFactory.getFakeCustomer(),
+    );
+  }
+}
diff --git a/src/test/common/mock/mock-di.ts b/src/test/common/mock/mock-di.ts
index 3bb7d4e..6e7b03d 100644
--- a/src/test/common/mock/mock-di.ts
+++ b/src/test/common/mock/mock-di.ts
@@ -1,7 +1,7 @@
-import di from "@/bootstrap/di/init-di"
-import * as serverDi from "@/feature/common/server-di"
+import di from "@/bootstrap/di/init-di";
+import * as serverDi from "@/feature/common/server-di";
 
 export default function mockDi() {
-    vi.spyOn(serverDi, "default").mockReturnValue(di)
-    return di
-}
\ No newline at end of file
+  vi.spyOn(serverDi, "default").mockReturnValue(di);
+  return di;
+}
diff --git a/src/test/setup.ts b/src/test/setup.ts
index 48d5e07..fded23a 100644
--- a/src/test/setup.ts
+++ b/src/test/setup.ts
@@ -1 +1 @@
-import "reflect-metadata";
\ No newline at end of file
+import "reflect-metadata";
diff --git a/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts b/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
index 9ce5ccd..437f9c9 100644
--- a/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
+++ b/src/test/unit/feature/customer/domain/usecase/fetch-customers-usecase.test.ts
@@ -1,48 +1,53 @@
-import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
+import CustomerRepo, {
+  customerRepoKey,
+} from "@/feature/core/customer/domain/i-repo/customer-repo";
 import { getMock } from "@/test/common/mock/mock-factory";
 import { describe } from "vitest";
 import { faker } from "@faker-js/faker";
 import CustomerFakeFactory from "@/test/common/fake-factory/customer/customer-fake-factory";
 import fetchCustomersUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-usecase";
 import mockDi from "@/test/common/mock/mock-di";
+import { right } from "fp-ts/lib/TaskEither";
 /* -------------------------------------------------------------------------- */
 /*                                   Faking                                   */
 /* -------------------------------------------------------------------------- */
-const fakedCustomerList = CustomerFakeFactory.getFakeCustomerList()
+const fakedCustomerList = CustomerFakeFactory.getFakeCustomerList();
 /* -------------------------------------------------------------------------- */
 /*                                   Mocking                                  */
 /* -------------------------------------------------------------------------- */
-const customerDi = mockDi()
+const customerDi = mockDi();
 
-const mockedFetchList = vi.fn<CustomerRepo['fetchList']>()
-const MockedRepo = getMock<CustomerRepo>()
-MockedRepo.setup((instance) => instance.fetchList).returns(mockedFetchList)
+const mockedFetchList = vi.fn<CustomerRepo["fetchList"]>();
+const MockedRepo = getMock<CustomerRepo>();
+MockedRepo.setup((instance) => instance.fetchList).returns(mockedFetchList);
 /* -------------------------------------------------------------------------- */
 /*                                     DI                                     */
 /* -------------------------------------------------------------------------- */
 customerDi.register(fetchCustomersUsecase.name, {
-    useValue: fetchCustomersUsecase
-})
+  useValue: fetchCustomersUsecase,
+});
 customerDi.register(customerRepoKey, {
-    useValue: MockedRepo.object()
-})
+  useValue: MockedRepo.object(),
+});
 /* -------------------------------------------------------------------------- */
 /*                                   Testing                                  */
 /* -------------------------------------------------------------------------- */
-const usecase = customerDi.resolve<typeof fetchCustomersUsecase>(fetchCustomersUsecase.name)
+const usecase = customerDi.resolve<typeof fetchCustomersUsecase>(
+  fetchCustomersUsecase.name,
+);
 describe("Fetch customers", () => {
-    describe("On given query string", () => {
-        const fakedQuery = faker.person.fullName(); 
-        describe("And returning list from repo", () => {
-            beforeEach(() => {
-                mockedFetchList.mockResolvedValue(fakedCustomerList)
-            })
-            it("Then should return correct list of customers", async () => {
-              // ! Act
-              const response = await usecase(fakedQuery)
-              // ? Assert
-              expect(response).toEqual(fakedCustomerList)
-            })
-        });
+  describe("On given query string", () => {
+    const fakedQuery = faker.person.fullName();
+    describe("And returning list from repo", () => {
+      beforeEach(() => {
+        mockedFetchList.mockResolvedValue(right(fakedCustomerList));
+      });
+      it("Then should return correct list of customers", async () => {
+        // ! Act
+        const response = await usecase(fakedQuery);
+        // ? Assert
+        expect(response).toEqual(fakedCustomerList);
+      });
     });
-});
\ No newline at end of file
+  });
+});
diff --git a/yarn.lock b/yarn.lock
index cac9504..32d67a6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -620,6 +620,11 @@
   resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
   integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
 
+"@pkgr/core@^0.1.0":
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
+  integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
+
 "@radix-ui/react-compose-refs@1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74"
@@ -1465,6 +1470,11 @@ concat-map@0.0.1:
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
   integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
 
+confusing-browser-globals@^1.0.10:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81"
+  integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==
+
 console-control-strings@^1.0.0, console-control-strings@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
@@ -1835,6 +1845,25 @@ escape-string-regexp@^4.0.0:
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
 
+eslint-config-airbnb-base@^15.0.0:
+  version "15.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236"
+  integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==
+  dependencies:
+    confusing-browser-globals "^1.0.10"
+    object.assign "^4.1.2"
+    object.entries "^1.1.5"
+    semver "^6.3.0"
+
+eslint-config-airbnb@^19.0.4:
+  version "19.0.4"
+  resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz#84d4c3490ad70a0ffa571138ebcdea6ab085fdc3"
+  integrity sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==
+  dependencies:
+    eslint-config-airbnb-base "^15.0.0"
+    object.assign "^4.1.2"
+    object.entries "^1.1.5"
+
 eslint-config-next@15.0.1:
   version "15.0.1"
   resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-15.0.1.tgz#5f49a01d312420cdbf1e87299396ef779ae99004"
@@ -1851,6 +1880,16 @@ eslint-config-next@15.0.1:
     eslint-plugin-react "^7.35.0"
     eslint-plugin-react-hooks "^5.0.0"
 
+eslint-config-prettier@^9.1.0:
+  version "9.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f"
+  integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==
+
+eslint-import-resolver-alias@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz#297062890e31e4d6651eb5eba9534e1f6e68fc97"
+  integrity sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==
+
 eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9:
   version "0.3.9"
   resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac"
@@ -1860,7 +1899,7 @@ eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9:
     is-core-module "^2.13.0"
     resolve "^1.22.4"
 
-eslint-import-resolver-typescript@^3.5.2:
+eslint-import-resolver-typescript@^3.5.2, eslint-import-resolver-typescript@^3.6.3:
   version "3.6.3"
   resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz#bb8e388f6afc0f940ce5d2c5fd4a3d147f038d9e"
   integrity sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==
@@ -1927,6 +1966,14 @@ eslint-plugin-jsx-a11y@^6.10.0:
     safe-regex-test "^1.0.3"
     string.prototype.includes "^2.0.1"
 
+eslint-plugin-prettier@^5.2.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz#d1c8f972d8f60e414c25465c163d16f209411f95"
+  integrity sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==
+  dependencies:
+    prettier-linter-helpers "^1.0.0"
+    synckit "^0.9.1"
+
 eslint-plugin-react-hooks@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz#72e2eefbac4b694f5324154619fee44f5f60f101"
@@ -2063,6 +2110,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
   integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
 
+fast-diff@^1.1.2:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
+  integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
+
 fast-glob@3.3.1:
   version "3.3.1"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4"
@@ -3107,7 +3159,7 @@ object-keys@^1.1.1:
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
   integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
 
-object.assign@^4.1.4, object.assign@^4.1.5:
+object.assign@^4.1.2, object.assign@^4.1.4, object.assign@^4.1.5:
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
   integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
@@ -3117,7 +3169,7 @@ object.assign@^4.1.4, object.assign@^4.1.5:
     has-symbols "^1.0.3"
     object-keys "^1.1.1"
 
-object.entries@^1.1.8:
+object.entries@^1.1.5, object.entries@^1.1.8:
   version "1.1.8"
   resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41"
   integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==
@@ -3341,6 +3393,18 @@ prelude-ls@^1.2.1:
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
   integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
 
+prettier-linter-helpers@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+  integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+  dependencies:
+    fast-diff "^1.1.2"
+
+prettier@^3.3.3:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
+  integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
+
 pretty-format@^27.0.2:
   version "27.5.1"
   resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
@@ -3589,7 +3653,7 @@ scheduler@0.25.0-rc-69d4b800-20241021:
   resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-69d4b800-20241021.tgz#336e47ef2bd5eddb0ebacfd910b5df1b236d92bd"
   integrity sha512-S5AYX/YhMAN6u9AXgKYbZP4U4ZklC6R9Q7HmFSBk7d4DLiHVNxvAvlSvuM4nxFkwOk50MnpfTKQ7UWHXDOc9Eg==
 
-semver@^6.0.0, semver@^6.3.1:
+semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
   version "6.3.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
   integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
@@ -3884,6 +3948,14 @@ symbol-tree@^3.2.4:
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
   integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
 
+synckit@^0.9.1:
+  version "0.9.2"
+  resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62"
+  integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==
+  dependencies:
+    "@pkgr/core" "^0.1.0"
+    tslib "^2.6.2"
+
 tailwind-merge@^2.5.4:
   version "2.5.4"
   resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.5.4.tgz#4bf574e81fa061adeceba099ae4df56edcee78d1"
@@ -4041,7 +4113,7 @@ tsconfig-paths@^3.15.0:
     minimist "^1.2.6"
     strip-bom "^3.0.0"
 
-tslib@*, tslib@2, tslib@^2.4.0:
+tslib@*, tslib@2, tslib@^2.4.0, tslib@^2.6.2:
   version "2.8.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
   integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
-- 
2.39.5


From d6fb515d61f806b5c7ebc068045f096e80d205b9 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 15 Nov 2024 19:54:29 +0300
Subject: [PATCH 34/37] Fix domain issues

---
 package.json                                                 | 1 +
 .../dashboard/components/server/cards/cards-controller.ts    | 2 ++
 .../server/latest-invoices/latest-invoices-controller.ts     | 2 ++
 .../domain/usecase/fetch-customer-invoices-usecase.ts        | 5 ++---
 .../domain/usecase/fetch-customers-amount-usecase.ts         | 1 +
 .../core/customer/domain/usecase/fetch-customers-usecase.ts  | 5 +----
 .../domain/usecase/fetch-all-invoices-amount-usecase.ts      | 5 ++---
 .../invoice/domain/usecase/fetch-invoices-status-summary.ts  | 3 ++-
 .../core/revenue/domain/usecase/fetch-revenues-usecase.ts    | 3 ++-
 .../domain/usecase/fetch-summary-info-usecase.ts             | 4 +---
 yarn.lock                                                    | 5 +++++
 11 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/package.json b/package.json
index 3223cf1..ad7a6a0 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
     "react-dom": "19.0.0-rc-69d4b800-20241021",
     "react-i18next": "^15.1.0",
     "reflect-metadata": "^0.2.2",
+    "server-only": "^0.0.1",
     "tailwind-merge": "^2.5.4",
     "tailwindcss-animate": "^1.0.7",
     "tsyringe": "^4.8.0",
diff --git a/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts b/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts
index 53c601a..9d38e29 100644
--- a/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts
+++ b/src/app/[lang]/dashboard/components/server/cards/cards-controller.ts
@@ -1,5 +1,7 @@
 import fetchSummaryInfoUsecase from "@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase";
+import { connection } from "next/server";
 
 export default function cardsController() {
+  connection();
   return fetchSummaryInfoUsecase();
 }
diff --git a/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
index 9f34a9a..2b7df56 100644
--- a/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
+++ b/src/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller.ts
@@ -1,5 +1,7 @@
 import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
+import { connection } from "next/server";
 
 export default function latestInvoicesController() {
+  connection();
   return fetchCustomerInvoicesUsecase();
 }
diff --git a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
index a2bd48e..7109cef 100644
--- a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
+++ b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts
@@ -1,3 +1,4 @@
+import "server-only";
 import { ApiEither } from "@/feature/common/data/api-task";
 import serverDi from "@/feature/common/server-di";
 import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
@@ -5,12 +6,10 @@ import CustomerInvoiceRepo, {
   customerInvoiceRepoKey,
 } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
 import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
-import { connection } from "next/server";
 
-export default async function fetchCustomerInvoicesUsecase(): Promise<
+export default function fetchCustomerInvoicesUsecase(): Promise<
   ApiEither<CustomerInvoice[]>
 > {
-  connection();
   const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(
     customerInvoiceRepoKey,
   );
diff --git a/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
index 2f5f620..e184514 100644
--- a/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-amount-usecase.ts
@@ -1,3 +1,4 @@
+import "server-only";
 import serverDi from "@/feature/common/server-di";
 import { customerKey } from "@/feature/core/customer/customer-key";
 import CustomerRepo, {
diff --git a/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
index 716b190..0418725 100644
--- a/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
+++ b/src/feature/core/customer/domain/usecase/fetch-customers-usecase.ts
@@ -1,5 +1,4 @@
-"use server";
-
+import "server-only";
 import { ApiEither } from "@/feature/common/data/api-task";
 import serverDi from "@/feature/common/server-di";
 import { customerKey } from "@/feature/core/customer/customer-key";
@@ -7,12 +6,10 @@ import Customer from "@/feature/core/customer/domain/entity/customer";
 import CustomerRepo, {
   customerRepoKey,
 } from "@/feature/core/customer/domain/i-repo/customer-repo";
-import { connection } from "next/server";
 
 export default function fetchCustomersUsecase(
   query: string,
 ): Promise<ApiEither<Customer[]>> {
-  connection();
   const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey);
 
   return repo.fetchList(query)();
diff --git a/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts b/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
index 8b8d04a..77aa180 100644
--- a/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
+++ b/src/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase.ts
@@ -1,12 +1,11 @@
-"use server";
-
+import "server-only";
 import serverDi from "@/feature/common/server-di";
 import InvoiceRepo, {
   invoiceRepoKey,
 } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 
-export default async function fetchAllInvoicesAmountUsecase(): Promise<number> {
+export default function fetchAllInvoicesAmountUsecase(): Promise<number> {
   const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey);
 
   return repo.fetchAllInvoicesAmount();
diff --git a/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts b/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
index 68c96ad..6ef1a31 100644
--- a/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
+++ b/src/feature/core/invoice/domain/usecase/fetch-invoices-status-summary.ts
@@ -1,3 +1,4 @@
+import "server-only";
 import serverDi from "@/feature/common/server-di";
 import InvoiceRepo, {
   invoiceRepoKey,
@@ -5,7 +6,7 @@ import InvoiceRepo, {
 import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
 import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
 
-export default async function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
+export default function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
   const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey);
   return repo.fetchInvoicesStatusSummary();
 }
diff --git a/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
index 57b5a5d..ed1812c 100644
--- a/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
+++ b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts
@@ -1,3 +1,4 @@
+import "server-only";
 import serverDi from "@/feature/common/server-di";
 import Revenue from "@/feature/core/revenue/domain/entity/revenue";
 import RevenueRepo, {
@@ -5,7 +6,7 @@ import RevenueRepo, {
 } from "@/feature/core/revenue/domain/i-repo/revenue-repo";
 import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
 
-export default async function fetchRevenuesUsecase(): Promise<Revenue[]> {
+export default function fetchRevenuesUsecase(): Promise<Revenue[]> {
   const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueRepoKey);
   return repo.fetchRevenues();
 }
diff --git a/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts b/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
index 33bbec4..84af863 100644
--- a/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
+++ b/src/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase.ts
@@ -1,14 +1,12 @@
+import "server-only";
 import serverDi from "@/feature/common/server-di";
 import fetchCustomersAmountUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-amount-usecase";
 import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase";
 import fetchInvoicesStatusSummary from "@/feature/core/invoice/domain/usecase/fetch-invoices-status-summary";
 import { summaryInfoModuleKey } from "@/feature/core/summary-info/domain/summary-info-module-key";
 import SummaryInfo from "@/feature/core/summary-info/domain/value-object/summary-info";
-import { connection } from "next/server";
 
 export default async function fetchSummaryInfoUsecase(): Promise<SummaryInfo> {
-  connection();
-
   try {
     const summaryInfoDi = serverDi(summaryInfoModuleKey);
     const invoicesAmountPromise = summaryInfoDi.resolve<
diff --git a/yarn.lock b/yarn.lock
index 32d67a6..827d025 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3663,6 +3663,11 @@ semver@^7.3.5, semver@^7.6.0, semver@^7.6.3:
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
   integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
 
+server-only@^0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/server-only/-/server-only-0.0.1.tgz#0f366bb6afb618c37c9255a314535dc412cd1c9e"
+  integrity sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==
+
 set-blocking@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
-- 
2.39.5


From 6b9c2d3925c676dd4f04888da1a4b3fffe1deaff Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 15 Nov 2024 20:25:42 +0300
Subject: [PATCH 35/37] Fix i18n issue for html

---
 src/app/[lang]/layout.tsx  | 27 +++++++++++++++++++++++----
 src/app/layout.tsx         | 24 ++----------------------
 src/bootstrap/i18n/i18n.ts |  4 +++-
 3 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/src/app/[lang]/layout.tsx b/src/app/[lang]/layout.tsx
index 4fbaf40..b142672 100644
--- a/src/app/[lang]/layout.tsx
+++ b/src/app/[lang]/layout.tsx
@@ -1,15 +1,34 @@
 import { initI18next } from "@/bootstrap/i18n/i18n";
 import TranslationsProvider from "@/bootstrap/i18n/i18n-provider";
+import localFont from "next/font/local";
 import { PropsWithChildren } from "react";
 
+const geistSans = localFont({
+  src: "./../fonts/GeistVF.woff",
+  variable: "--font-geist-sans",
+  weight: "100 900",
+});
+const geistMono = localFont({
+  src: "./../fonts/GeistMonoVF.woff",
+  variable: "--font-geist-mono",
+  weight: "100 900",
+});
+
 export default async function layout(
   props: PropsWithChildren & { params: Promise<{ lang: string }> },
 ) {
-  const { lang } = await props.params;
+  const { params, children } = props;
+  const { lang } = await params;
   const { resources } = await initI18next({ lng: lang });
   return (
-    <TranslationsProvider lng={lang} resources={resources}>
-      {props.children}
-    </TranslationsProvider>
+    <html lang={lang}>
+      <body
+        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
+      >
+        <TranslationsProvider lng={lang} resources={resources}>
+          {children}
+        </TranslationsProvider>
+      </body>
+    </html>
   );
 }
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index a36cde0..c277050 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,35 +1,15 @@
 import type { Metadata } from "next";
-import localFont from "next/font/local";
 import "./globals.css";
 
-const geistSans = localFont({
-  src: "./fonts/GeistVF.woff",
-  variable: "--font-geist-sans",
-  weight: "100 900",
-});
-const geistMono = localFont({
-  src: "./fonts/GeistMonoVF.woff",
-  variable: "--font-geist-mono",
-  weight: "100 900",
-});
-
 export const metadata: Metadata = {
   title: "Create Next App",
   description: "Generated by create next app",
 };
 
-export default function RootLayout({
+export default async function RootLayout({
   children,
 }: Readonly<{
   children: React.ReactNode;
 }>) {
-  return (
-    <html lang="en">
-      <body
-        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
-      >
-        {children}
-      </body>
-    </html>
-  );
+  return children;
 }
diff --git a/src/bootstrap/i18n/i18n.ts b/src/bootstrap/i18n/i18n.ts
index 8b348f2..c95d601 100644
--- a/src/bootstrap/i18n/i18n.ts
+++ b/src/bootstrap/i18n/i18n.ts
@@ -3,6 +3,8 @@ import { createInstance, i18n, Resource } from "i18next";
 import resourcesToBackend from "i18next-resources-to-backend";
 import { initReactI18next } from "react-i18next/initReactI18next";
 
+const initI18nextInstance = createInstance();
+
 export const initI18next = async (params: {
   lng: string;
   i18n?: i18n;
@@ -10,7 +12,7 @@ export const initI18next = async (params: {
   ns?: string;
 }) => {
   const { lng, i18n, ns, resources } = params;
-  const i18nInstance = i18n || createInstance();
+  const i18nInstance = i18n || initI18nextInstance;
   await i18nInstance
     .use(initReactI18next)
     .use(
-- 
2.39.5


From 2365a07d76aa28a7913324b82aa96aa755c795c1 Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Fri, 15 Nov 2024 20:46:17 +0300
Subject: [PATCH 36/37] Add docker file and docker compose file

---
 Dockerfile                                    |   70 +
 docker-compose.yml                            |   27 +
 next.config.ts                                |    1 +
 package-lock.json                             | 5724 -----------------
 .../common/failures/failure-helpers.ts        |   30 +-
 5 files changed, 115 insertions(+), 5737 deletions(-)
 create mode 100644 Dockerfile
 create mode 100644 docker-compose.yml
 delete mode 100644 package-lock.json

diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..36923cd
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,70 @@
+# syntax=docker.io/docker/dockerfile:1
+
+FROM node:18-alpine AS base
+
+# Install dependencies only when needed
+FROM base AS deps
+# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+
+# Install dependencies based on the preferred package manager
+COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
+RUN \
+  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
+  elif [ -f package-lock.json ]; then npm ci; \
+  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
+  else echo "Lockfile not found." && exit 1; \
+  fi
+
+
+# Rebuild the source code only when needed
+FROM base AS builder
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+
+# Next.js collects completely anonymous telemetry data about general usage.
+# Learn more here: https://nextjs.org/telemetry
+# Uncomment the following line in case you want to disable telemetry during the build.
+# ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN \
+  if [ -f yarn.lock ]; then yarn run build; \
+  elif [ -f package-lock.json ]; then npm run build; \
+  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
+  else echo "Lockfile not found." && exit 1; \
+  fi
+
+# Production image, copy all the files and run next
+FROM base AS runner
+WORKDIR /app
+
+ENV NODE_ENV=production
+# Uncomment the following line in case you want to disable telemetry during runtime.
+# ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+COPY --from=builder /app/public ./public
+
+# Set the correct permission for prerender cache
+RUN mkdir .next
+RUN chown nextjs:nodejs .next
+
+# Automatically leverage output traces to reduce image size
+# https://nextjs.org/docs/advanced-features/output-file-tracing
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+USER nextjs
+
+EXPOSE 3000
+
+ENV PORT=3000
+
+# server.js is created by next build from the standalone output
+# https://nextjs.org/docs/pages/api-reference/next-config-js/output
+ENV HOSTNAME="0.0.0.0"
+CMD ["node", "server.js"]
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..9e2981f
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,27 @@
+services:
+  db:
+    image: postgres
+    restart: always
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+    ports:
+      - 5432:5432
+    environment:
+      POSTGRES_PASSWORD: example
+      POSTGRES_USER: admin
+      POSTGRES_DB: nextbp 
+
+
+  app:
+    build: .
+    ports:
+      - 3000:3000
+    environment:
+      POSTGRES_HOST: db
+      POSTGRES_PORT: 5432
+      POSTGRES_USER: admin
+      POSTGRES_PASS: example
+      POSTGRES_DB: nextbp
+volumes:
+  postgres_data:
+    
\ No newline at end of file
diff --git a/next.config.ts b/next.config.ts
index e9ffa30..ed23578 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -2,6 +2,7 @@ import type { NextConfig } from "next";
 
 const nextConfig: NextConfig = {
   /* config options here */
+  output: "standalone",
 };
 
 export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index d6c0f4e..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,5724 +0,0 @@
-{
-  "name": "nextjs-boilerplate",
-  "version": "0.1.0",
-  "lockfileVersion": 3,
-  "requires": true,
-  "packages": {
-    "": {
-      "name": "nextjs-boilerplate",
-      "version": "0.1.0",
-      "dependencies": {
-        "next": "15.0.1",
-        "react": "19.0.0-rc-69d4b800-20241021",
-        "react-dom": "19.0.0-rc-69d4b800-20241021"
-      },
-      "devDependencies": {
-        "@types/node": "^20",
-        "@types/react": "^18",
-        "@types/react-dom": "^18",
-        "eslint": "^8",
-        "eslint-config-next": "15.0.1",
-        "postcss": "^8",
-        "tailwindcss": "^3.4.1",
-        "typescript": "^5"
-      }
-    },
-    "node_modules/@alloc/quick-lru": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
-      "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/@emnapi/runtime": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
-      "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
-      "license": "MIT",
-      "optional": true,
-      "dependencies": {
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@eslint-community/eslint-utils": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
-      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-visitor-keys": "^3.3.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "peerDependencies": {
-        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
-      }
-    },
-    "node_modules/@eslint-community/regexpp": {
-      "version": "4.11.1",
-      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz",
-      "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
-      "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.12.4",
-        "debug": "^4.3.2",
-        "espree": "^9.6.0",
-        "globals": "^13.19.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.0",
-        "minimatch": "^3.1.2",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint/js": {
-      "version": "8.57.1",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
-      "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      }
-    },
-    "node_modules/@humanwhocodes/config-array": {
-      "version": "0.13.0",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
-      "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
-      "deprecated": "Use @eslint/config-array instead",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@humanwhocodes/object-schema": "^2.0.3",
-        "debug": "^4.3.1",
-        "minimatch": "^3.0.5"
-      },
-      "engines": {
-        "node": ">=10.10.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/object-schema": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
-      "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
-      "deprecated": "Use @eslint/object-schema instead",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/@img/sharp-darwin-arm64": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
-      "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-darwin-arm64": "1.0.4"
-      }
-    },
-    "node_modules/@img/sharp-darwin-x64": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
-      "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-darwin-x64": "1.0.4"
-      }
-    },
-    "node_modules/@img/sharp-libvips-darwin-arm64": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
-      "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-darwin-x64": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
-      "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linux-arm": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
-      "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
-      "cpu": [
-        "arm"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linux-arm64": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
-      "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linux-s390x": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
-      "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
-      "cpu": [
-        "s390x"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linux-x64": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
-      "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
-      "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linuxmusl-x64": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
-      "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-linux-arm": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
-      "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
-      "cpu": [
-        "arm"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linux-arm": "1.0.5"
-      }
-    },
-    "node_modules/@img/sharp-linux-arm64": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
-      "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linux-arm64": "1.0.4"
-      }
-    },
-    "node_modules/@img/sharp-linux-s390x": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
-      "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
-      "cpu": [
-        "s390x"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linux-s390x": "1.0.4"
-      }
-    },
-    "node_modules/@img/sharp-linux-x64": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
-      "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linux-x64": "1.0.4"
-      }
-    },
-    "node_modules/@img/sharp-linuxmusl-arm64": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
-      "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
-      }
-    },
-    "node_modules/@img/sharp-linuxmusl-x64": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
-      "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
-      }
-    },
-    "node_modules/@img/sharp-wasm32": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
-      "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
-      "cpu": [
-        "wasm32"
-      ],
-      "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
-      "optional": true,
-      "dependencies": {
-        "@emnapi/runtime": "^1.2.0"
-      },
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-win32-ia32": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
-      "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
-      "cpu": [
-        "ia32"
-      ],
-      "license": "Apache-2.0 AND LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-win32-x64": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
-      "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "Apache-2.0 AND LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "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.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
-      "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
-      }
-    },
-    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
-      "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
-      "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/@jridgewell/gen-mapping": {
-      "version": "0.3.5",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
-      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/set-array": "^1.2.1",
-        "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "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/set-array": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
-      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/sourcemap-codec": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
-      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.25",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
-      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/resolve-uri": "^3.1.0",
-        "@jridgewell/sourcemap-codec": "^1.4.14"
-      }
-    },
-    "node_modules/@next/env": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/env/-/env-15.0.1.tgz",
-      "integrity": "sha512-lc4HeDUKO9gxxlM5G2knTRifqhsY6yYpwuHspBZdboZe0Gp+rZHBNNSIjmQKDJIdRXiXGyVnSD6gafrbQPvILQ==",
-      "license": "MIT"
-    },
-    "node_modules/@next/eslint-plugin-next": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.1.tgz",
-      "integrity": "sha512-bKWsMaGPbiFAaGqrDJvbE8b4Z0uKicGVcgOI77YM2ui3UfjHMr4emFPrZTLeZVchi7fT1mooG2LxREfUUClIKw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-glob": "3.3.1"
-      }
-    },
-    "node_modules/@next/swc-darwin-arm64": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.1.tgz",
-      "integrity": "sha512-C9k/Xv4sxkQRTA37Z6MzNq3Yb1BJMmSqjmwowoWEpbXTkAdfOwnoKOpAb71ItSzoA26yUTIo6ZhN8rKGu4ExQw==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-darwin-x64": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.1.tgz",
-      "integrity": "sha512-uHl13HXOuq1G7ovWFxCACDJHTSDVbn/sbLv8V1p+7KIvTrYQ5HNoSmKBdYeEKRRCbEmd+OohOgg9YOp8Ux3MBg==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-linux-arm64-gnu": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.1.tgz",
-      "integrity": "sha512-LvyhvxHOihFTEIbb35KxOc3q8w8G4xAAAH/AQnsYDEnOvwawjL2eawsB59AX02ki6LJdgDaHoTEnC54Gw+82xw==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-linux-arm64-musl": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.1.tgz",
-      "integrity": "sha512-vFmCGUFNyk/A5/BYcQNhAQqPIw01RJaK6dRO+ZEhz0DncoW+hJW1kZ8aH2UvTX27zPq3m85zN5waMSbZEmANcQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-linux-x64-gnu": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.1.tgz",
-      "integrity": "sha512-5by7IYq0NCF8rouz6Qg9T97jYU68kaClHPfGpQG2lCZpSYHtSPQF1kjnqBTd34RIqPKMbCa4DqCufirgr8HM5w==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-linux-x64-musl": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.1.tgz",
-      "integrity": "sha512-lmYr6H3JyDNBJLzklGXLfbehU3ay78a+b6UmBGlHls4xhDXBNZfgb0aI67sflrX+cGBnv1LgmWzFlYrAYxS1Qw==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-win32-arm64-msvc": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.1.tgz",
-      "integrity": "sha512-DS8wQtl6diAj0eZTdH0sefykm4iXMbHT4MOvLwqZiIkeezKpkgPFcEdFlz3vKvXa2R/2UEgMh48z1nEpNhjeOQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-win32-x64-msvc": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.1.tgz",
-      "integrity": "sha512-4Ho2ggvDdMKlZ/0e9HNdZ9ngeaBwtc+2VS5oCeqrbXqOgutX6I4U2X/42VBw0o+M5evn4/7v3zKgGHo+9v/VjA==",
-      "cpu": [
-        "x64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "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/@nolyfill/is-core-module": {
-      "version": "1.0.39",
-      "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
-      "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=12.4.0"
-      }
-    },
-    "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/@rtsao/scc": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
-      "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@rushstack/eslint-patch": {
-      "version": "1.10.4",
-      "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz",
-      "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@swc/counter": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
-      "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
-      "license": "Apache-2.0"
-    },
-    "node_modules/@swc/helpers": {
-      "version": "0.5.13",
-      "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz",
-      "integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==",
-      "license": "Apache-2.0",
-      "dependencies": {
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@types/json5": {
-      "version": "0.0.29",
-      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
-      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/node": {
-      "version": "20.17.0",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.0.tgz",
-      "integrity": "sha512-a7zRo0f0eLo9K5X9Wp5cAqTUNGzuFLDG2R7C4HY2BhcMAsxgSPuRvAC1ZB6QkuUQXf0YZAgfOX2ZyrBa2n4nHQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "undici-types": "~6.19.2"
-      }
-    },
-    "node_modules/@types/prop-types": {
-      "version": "15.7.13",
-      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
-      "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/react": {
-      "version": "18.3.12",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz",
-      "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/prop-types": "*",
-        "csstype": "^3.0.2"
-      }
-    },
-    "node_modules/@types/react-dom": {
-      "version": "18.3.1",
-      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz",
-      "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/react": "*"
-      }
-    },
-    "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz",
-      "integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.11.0",
-        "@typescript-eslint/type-utils": "8.11.0",
-        "@typescript-eslint/utils": "8.11.0",
-        "@typescript-eslint/visitor-keys": "8.11.0",
-        "graphemer": "^1.4.0",
-        "ignore": "^5.3.1",
-        "natural-compare": "^1.4.0",
-        "ts-api-utils": "^1.3.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
-        "eslint": "^8.57.0 || ^9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/parser": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz",
-      "integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "@typescript-eslint/scope-manager": "8.11.0",
-        "@typescript-eslint/types": "8.11.0",
-        "@typescript-eslint/typescript-estree": "8.11.0",
-        "@typescript-eslint/visitor-keys": "8.11.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": {
-        "eslint": "^8.57.0 || ^9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz",
-      "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "8.11.0",
-        "@typescript-eslint/visitor-keys": "8.11.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/type-utils": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz",
-      "integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/typescript-estree": "8.11.0",
-        "@typescript-eslint/utils": "8.11.0",
-        "debug": "^4.3.4",
-        "ts-api-utils": "^1.3.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/types": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz",
-      "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==",
-      "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.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz",
-      "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "@typescript-eslint/types": "8.11.0",
-        "@typescript-eslint/visitor-keys": "8.11.0",
-        "debug": "^4.3.4",
-        "fast-glob": "^3.3.2",
-        "is-glob": "^4.0.3",
-        "minimatch": "^9.0.4",
-        "semver": "^7.6.0",
-        "ts-api-utils": "^1.3.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
-      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
-      "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.4"
-      },
-      "engines": {
-        "node": ">=8.6.0"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/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/@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/utils": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz",
-      "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/eslint-utils": "^4.4.0",
-        "@typescript-eslint/scope-manager": "8.11.0",
-        "@typescript-eslint/types": "8.11.0",
-        "@typescript-eslint/typescript-estree": "8.11.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz",
-      "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "8.11.0",
-        "eslint-visitor-keys": "^3.4.3"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@ungap/structured-clone": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
-      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/acorn": {
-      "version": "8.13.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz",
-      "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==",
-      "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-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "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/any-promise": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
-      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "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/arg": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
-      "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "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/aria-query": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
-      "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/array-buffer-byte-length": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
-      "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.5",
-        "is-array-buffer": "^3.0.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array-includes": {
-      "version": "3.1.8",
-      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
-      "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-object-atoms": "^1.0.0",
-        "get-intrinsic": "^1.2.4",
-        "is-string": "^1.0.7"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.findlast": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
-      "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
-        "es-shim-unscopables": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.findlastindex": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
-      "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
-        "es-shim-unscopables": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.flat": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
-      "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "es-shim-unscopables": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.flatmap": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
-      "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "es-shim-unscopables": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.tosorted": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
-      "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.3",
-        "es-errors": "^1.3.0",
-        "es-shim-unscopables": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/arraybuffer.prototype.slice": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
-      "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-buffer-byte-length": "^1.0.1",
-        "call-bind": "^1.0.5",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.3",
-        "es-errors": "^1.2.1",
-        "get-intrinsic": "^1.2.3",
-        "is-array-buffer": "^3.0.4",
-        "is-shared-array-buffer": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/ast-types-flow": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
-      "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/available-typed-arrays": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
-      "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "possible-typed-array-names": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/axe-core": {
-      "version": "4.10.2",
-      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz",
-      "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==",
-      "dev": true,
-      "license": "MPL-2.0",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/axobject-query": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
-      "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "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/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/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "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==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fill-range": "^7.1.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/busboy": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
-      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
-      "dependencies": {
-        "streamsearch": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=10.16.0"
-      }
-    },
-    "node_modules/call-bind": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
-      "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "set-function-length": "^1.2.1"
-      },
-      "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/camelcase-css": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
-      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/caniuse-lite": {
-      "version": "1.0.30001669",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz",
-      "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==",
-      "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/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/chokidar/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/client-only": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
-      "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
-      "license": "MIT"
-    },
-    "node_modules/color": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
-      "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
-      "license": "MIT",
-      "optional": true,
-      "dependencies": {
-        "color-convert": "^2.0.1",
-        "color-string": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=12.5.0"
-      }
-    },
-    "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==",
-      "devOptional": 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==",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/color-string": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
-      "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
-      "license": "MIT",
-      "optional": true,
-      "dependencies": {
-        "color-name": "^1.0.0",
-        "simple-swizzle": "^0.2.2"
-      }
-    },
-    "node_modules/commander": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
-      "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "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/cross-spawn": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "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.1.3",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
-      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/damerau-levenshtein": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
-      "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
-      "dev": true,
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/data-view-buffer": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
-      "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.6",
-        "es-errors": "^1.3.0",
-        "is-data-view": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/data-view-byte-length": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
-      "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "es-errors": "^1.3.0",
-        "is-data-view": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/data-view-byte-offset": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
-      "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.6",
-        "es-errors": "^1.3.0",
-        "is-data-view": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/debug": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
-      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.3"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
-    "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/define-data-property": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
-      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/define-properties": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
-      "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.0.1",
-        "has-property-descriptors": "^1.0.0",
-        "object-keys": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/detect-libc": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
-      "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
-      "license": "Apache-2.0",
-      "optional": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/didyoumean": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
-      "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
-      "dev": true,
-      "license": "Apache-2.0"
-    },
-    "node_modules/dlv": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
-      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/doctrine": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
-      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/eastasianwidth": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
-      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "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/enhanced-resolve": {
-      "version": "5.17.1",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
-      "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "graceful-fs": "^4.2.4",
-        "tapable": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/es-abstract": {
-      "version": "1.23.3",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
-      "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-buffer-byte-length": "^1.0.1",
-        "arraybuffer.prototype.slice": "^1.0.3",
-        "available-typed-arrays": "^1.0.7",
-        "call-bind": "^1.0.7",
-        "data-view-buffer": "^1.0.1",
-        "data-view-byte-length": "^1.0.1",
-        "data-view-byte-offset": "^1.0.0",
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
-        "es-set-tostringtag": "^2.0.3",
-        "es-to-primitive": "^1.2.1",
-        "function.prototype.name": "^1.1.6",
-        "get-intrinsic": "^1.2.4",
-        "get-symbol-description": "^1.0.2",
-        "globalthis": "^1.0.3",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2",
-        "has-proto": "^1.0.3",
-        "has-symbols": "^1.0.3",
-        "hasown": "^2.0.2",
-        "internal-slot": "^1.0.7",
-        "is-array-buffer": "^3.0.4",
-        "is-callable": "^1.2.7",
-        "is-data-view": "^1.0.1",
-        "is-negative-zero": "^2.0.3",
-        "is-regex": "^1.1.4",
-        "is-shared-array-buffer": "^1.0.3",
-        "is-string": "^1.0.7",
-        "is-typed-array": "^1.1.13",
-        "is-weakref": "^1.0.2",
-        "object-inspect": "^1.13.1",
-        "object-keys": "^1.1.1",
-        "object.assign": "^4.1.5",
-        "regexp.prototype.flags": "^1.5.2",
-        "safe-array-concat": "^1.1.2",
-        "safe-regex-test": "^1.0.3",
-        "string.prototype.trim": "^1.2.9",
-        "string.prototype.trimend": "^1.0.8",
-        "string.prototype.trimstart": "^1.0.8",
-        "typed-array-buffer": "^1.0.2",
-        "typed-array-byte-length": "^1.0.1",
-        "typed-array-byte-offset": "^1.0.2",
-        "typed-array-length": "^1.0.6",
-        "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.15"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/es-define-property": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
-      "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.2.4"
-      },
-      "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==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-iterator-helpers": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz",
-      "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.3",
-        "es-errors": "^1.3.0",
-        "es-set-tostringtag": "^2.0.3",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "globalthis": "^1.0.4",
-        "has-property-descriptors": "^1.0.2",
-        "has-proto": "^1.0.3",
-        "has-symbols": "^1.0.3",
-        "internal-slot": "^1.0.7",
-        "iterator.prototype": "^1.1.3",
-        "safe-array-concat": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-object-atoms": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
-      "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-set-tostringtag": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
-      "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.2.4",
-        "has-tostringtag": "^1.0.2",
-        "hasown": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-shim-unscopables": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
-      "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "hasown": "^2.0.0"
-      }
-    },
-    "node_modules/es-to-primitive": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
-      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-callable": "^1.1.4",
-        "is-date-object": "^1.0.1",
-        "is-symbol": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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/eslint": {
-      "version": "8.57.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
-      "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
-      "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/eslint-utils": "^4.2.0",
-        "@eslint-community/regexpp": "^4.6.1",
-        "@eslint/eslintrc": "^2.1.4",
-        "@eslint/js": "8.57.1",
-        "@humanwhocodes/config-array": "^0.13.0",
-        "@humanwhocodes/module-importer": "^1.0.1",
-        "@nodelib/fs.walk": "^1.2.8",
-        "@ungap/structured-clone": "^1.2.0",
-        "ajv": "^6.12.4",
-        "chalk": "^4.0.0",
-        "cross-spawn": "^7.0.2",
-        "debug": "^4.3.2",
-        "doctrine": "^3.0.0",
-        "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^7.2.2",
-        "eslint-visitor-keys": "^3.4.3",
-        "espree": "^9.6.1",
-        "esquery": "^1.4.2",
-        "esutils": "^2.0.2",
-        "fast-deep-equal": "^3.1.3",
-        "file-entry-cache": "^6.0.1",
-        "find-up": "^5.0.0",
-        "glob-parent": "^6.0.2",
-        "globals": "^13.19.0",
-        "graphemer": "^1.4.0",
-        "ignore": "^5.2.0",
-        "imurmurhash": "^0.1.4",
-        "is-glob": "^4.0.0",
-        "is-path-inside": "^3.0.3",
-        "js-yaml": "^4.1.0",
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.4.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.2",
-        "natural-compare": "^1.4.0",
-        "optionator": "^0.9.3",
-        "strip-ansi": "^6.0.1",
-        "text-table": "^0.2.0"
-      },
-      "bin": {
-        "eslint": "bin/eslint.js"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/eslint-config-next": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.0.1.tgz",
-      "integrity": "sha512-3cYCrgbH6GS/ufApza7XCKz92vtq4dAdYhx++rMFNlH2cAV+/GsAKkrr4+bohYOACmzG2nAOR+uWprKC1Uld6A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@next/eslint-plugin-next": "15.0.1",
-        "@rushstack/eslint-patch": "^1.10.3",
-        "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
-        "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
-        "eslint-import-resolver-node": "^0.3.6",
-        "eslint-import-resolver-typescript": "^3.5.2",
-        "eslint-plugin-import": "^2.31.0",
-        "eslint-plugin-jsx-a11y": "^6.10.0",
-        "eslint-plugin-react": "^7.35.0",
-        "eslint-plugin-react-hooks": "^5.0.0"
-      },
-      "peerDependencies": {
-        "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0",
-        "typescript": ">=3.3.1"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-import-resolver-node": {
-      "version": "0.3.9",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
-      "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^3.2.7",
-        "is-core-module": "^2.13.0",
-        "resolve": "^1.22.4"
-      }
-    },
-    "node_modules/eslint-import-resolver-node/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-import-resolver-typescript": {
-      "version": "3.6.3",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz",
-      "integrity": "sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "@nolyfill/is-core-module": "1.0.39",
-        "debug": "^4.3.5",
-        "enhanced-resolve": "^5.15.0",
-        "eslint-module-utils": "^2.8.1",
-        "fast-glob": "^3.3.2",
-        "get-tsconfig": "^4.7.5",
-        "is-bun-module": "^1.0.2",
-        "is-glob": "^4.0.3"
-      },
-      "engines": {
-        "node": "^14.18.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
-      },
-      "peerDependencies": {
-        "eslint": "*",
-        "eslint-plugin-import": "*",
-        "eslint-plugin-import-x": "*"
-      },
-      "peerDependenciesMeta": {
-        "eslint-plugin-import": {
-          "optional": true
-        },
-        "eslint-plugin-import-x": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-import-resolver-typescript/node_modules/fast-glob": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
-      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
-      "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.4"
-      },
-      "engines": {
-        "node": ">=8.6.0"
-      }
-    },
-    "node_modules/eslint-import-resolver-typescript/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/eslint-module-utils": {
-      "version": "2.12.0",
-      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
-      "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^3.2.7"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependenciesMeta": {
-        "eslint": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-module-utils/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-plugin-import": {
-      "version": "2.31.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
-      "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@rtsao/scc": "^1.1.0",
-        "array-includes": "^3.1.8",
-        "array.prototype.findlastindex": "^1.2.5",
-        "array.prototype.flat": "^1.3.2",
-        "array.prototype.flatmap": "^1.3.2",
-        "debug": "^3.2.7",
-        "doctrine": "^2.1.0",
-        "eslint-import-resolver-node": "^0.3.9",
-        "eslint-module-utils": "^2.12.0",
-        "hasown": "^2.0.2",
-        "is-core-module": "^2.15.1",
-        "is-glob": "^4.0.3",
-        "minimatch": "^3.1.2",
-        "object.fromentries": "^2.0.8",
-        "object.groupby": "^1.0.3",
-        "object.values": "^1.2.0",
-        "semver": "^6.3.1",
-        "string.prototype.trimend": "^1.0.8",
-        "tsconfig-paths": "^3.15.0"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependencies": {
-        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/doctrine": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/eslint-plugin-jsx-a11y": {
-      "version": "6.10.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.1.tgz",
-      "integrity": "sha512-zHByM9WTUMnfsDTafGXRiqxp6lFtNoSOWBY6FonVRn3A+BUwN1L/tdBXT40BcBJi0cZjOGTXZ0eD/rTG9fEJ0g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "aria-query": "^5.3.2",
-        "array-includes": "^3.1.8",
-        "array.prototype.flatmap": "^1.3.2",
-        "ast-types-flow": "^0.0.8",
-        "axe-core": "^4.10.0",
-        "axobject-query": "^4.1.0",
-        "damerau-levenshtein": "^1.0.8",
-        "emoji-regex": "^9.2.2",
-        "es-iterator-helpers": "^1.1.0",
-        "hasown": "^2.0.2",
-        "jsx-ast-utils": "^3.3.5",
-        "language-tags": "^1.0.9",
-        "minimatch": "^3.1.2",
-        "object.fromentries": "^2.0.8",
-        "safe-regex-test": "^1.0.3",
-        "string.prototype.includes": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=4.0"
-      },
-      "peerDependencies": {
-        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
-      }
-    },
-    "node_modules/eslint-plugin-react": {
-      "version": "7.37.2",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz",
-      "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-includes": "^3.1.8",
-        "array.prototype.findlast": "^1.2.5",
-        "array.prototype.flatmap": "^1.3.2",
-        "array.prototype.tosorted": "^1.1.4",
-        "doctrine": "^2.1.0",
-        "es-iterator-helpers": "^1.1.0",
-        "estraverse": "^5.3.0",
-        "hasown": "^2.0.2",
-        "jsx-ast-utils": "^2.4.1 || ^3.0.0",
-        "minimatch": "^3.1.2",
-        "object.entries": "^1.1.8",
-        "object.fromentries": "^2.0.8",
-        "object.values": "^1.2.0",
-        "prop-types": "^15.8.1",
-        "resolve": "^2.0.0-next.5",
-        "semver": "^6.3.1",
-        "string.prototype.matchall": "^4.0.11",
-        "string.prototype.repeat": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependencies": {
-        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
-      }
-    },
-    "node_modules/eslint-plugin-react-hooks": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz",
-      "integrity": "sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "peerDependencies": {
-        "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
-      }
-    },
-    "node_modules/eslint-plugin-react/node_modules/doctrine": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/eslint-plugin-react/node_modules/resolve": {
-      "version": "2.0.0-next.5",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
-      "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-core-module": "^2.13.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/eslint-plugin-react/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/eslint-scope": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
-      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "esrecurse": "^4.3.0",
-        "estraverse": "^5.2.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.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/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/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/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/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==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/fast-glob": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
-      "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
-      "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.4"
-      },
-      "engines": {
-        "node": ">=8.6.0"
-      }
-    },
-    "node_modules/fast-glob/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/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.17.1",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
-      "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "node_modules/file-entry-cache": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
-      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "flat-cache": "^3.0.4"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.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==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "to-regex-range": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "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-cache": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
-      "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "flatted": "^3.2.9",
-        "keyv": "^4.5.3",
-        "rimraf": "^3.0.2"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      }
-    },
-    "node_modules/flatted": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
-      "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/for-each": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
-      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-callable": "^1.1.3"
-      }
-    },
-    "node_modules/foreground-child": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
-      "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "cross-spawn": "^7.0.0",
-        "signal-exit": "^4.0.1"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "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==",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/function.prototype.name": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
-      "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "functions-have-names": "^1.2.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/functions-have-names": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
-      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/get-intrinsic": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
-      "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "has-proto": "^1.0.1",
-        "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/get-symbol-description": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
-      "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.5",
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/get-tsconfig": {
-      "version": "4.8.1",
-      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
-      "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "resolve-pkg-maps": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
-      }
-    },
-    "node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "deprecated": "Glob versions prior to v9 are no longer supported",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "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/globals": {
-      "version": "13.24.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
-      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "type-fest": "^0.20.2"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/globalthis": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
-      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-properties": "^1.2.1",
-        "gopd": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/gopd": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
-      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.1.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/graphemer": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
-      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/has-bigints": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
-      "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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-property-descriptors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
-      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-proto": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
-      "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-symbols": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
-      "dev": true,
-      "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==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-symbols": "^1.0.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/hasown": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
-      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "function-bind": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "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/import-fresh": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
-      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
-      "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/imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.19"
-      }
-    },
-    "node_modules/inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
-      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "node_modules/inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/internal-slot": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
-      "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "hasown": "^2.0.0",
-        "side-channel": "^1.0.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/is-array-buffer": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
-      "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-arrayish": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
-      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
-      "license": "MIT",
-      "optional": true
-    },
-    "node_modules/is-async-function": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
-      "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-bigint": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
-      "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-bigints": "^1.0.1"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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-boolean-object": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
-      "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-bun-module": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.2.1.tgz",
-      "integrity": "sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "semver": "^7.6.3"
-      }
-    },
-    "node_modules/is-callable": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
-      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-core-module": {
-      "version": "2.15.1",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
-      "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "hasown": "^2.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-data-view": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
-      "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-date-object": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
-      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-finalizationregistry": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz",
-      "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-generator-function": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
-      "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-extglob": "^2.1.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-map": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
-      "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-negative-zero": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
-      "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.12.0"
-      }
-    },
-    "node_modules/is-number-object": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
-      "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-regex": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
-      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-set": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
-      "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-shared-array-buffer": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
-      "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-string": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
-      "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-symbol": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
-      "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-symbols": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-typed-array": {
-      "version": "1.1.13",
-      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
-      "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "which-typed-array": "^1.1.14"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-weakmap": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
-      "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-weakref": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
-      "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-weakset": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz",
-      "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "get-intrinsic": "^1.2.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/isarray": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
-      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/iterator.prototype": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz",
-      "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-properties": "^1.2.1",
-        "get-intrinsic": "^1.2.1",
-        "has-symbols": "^1.0.3",
-        "reflect.getprototypeof": "^1.0.4",
-        "set-function-name": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "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/jiti": {
-      "version": "1.21.6",
-      "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
-      "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "jiti": "bin/jiti.js"
-      }
-    },
-    "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.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-      "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==",
-      "dev": true,
-      "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": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
-      "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "minimist": "^1.2.0"
-      },
-      "bin": {
-        "json5": "lib/cli.js"
-      }
-    },
-    "node_modules/jsx-ast-utils": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
-      "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-includes": "^3.1.6",
-        "array.prototype.flat": "^1.3.1",
-        "object.assign": "^4.1.4",
-        "object.values": "^1.1.6"
-      },
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "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==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "json-buffer": "3.0.1"
-      }
-    },
-    "node_modules/language-subtag-registry": {
-      "version": "0.3.23",
-      "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
-      "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
-      "dev": true,
-      "license": "CC0-1.0"
-    },
-    "node_modules/language-tags": {
-      "version": "1.0.9",
-      "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
-      "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "language-subtag-registry": "^0.3.20"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "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/lilconfig": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
-      "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/lines-and-columns": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
-      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "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.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/loose-envify": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
-      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "js-tokens": "^3.0.0 || ^4.0.0"
-      },
-      "bin": {
-        "loose-envify": "cli.js"
-      }
-    },
-    "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/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/micromatch": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
-      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "braces": "^3.0.3",
-        "picomatch": "^2.3.1"
-      },
-      "engines": {
-        "node": ">=8.6"
-      }
-    },
-    "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==",
-      "dev": true,
-      "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/ms": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/mz": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
-      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "any-promise": "^1.0.0",
-        "object-assign": "^4.0.1",
-        "thenify-all": "^1.0.0"
-      }
-    },
-    "node_modules/nanoid": {
-      "version": "3.3.7",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
-      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
-      "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/next": {
-      "version": "15.0.1",
-      "resolved": "https://registry.npmjs.org/next/-/next-15.0.1.tgz",
-      "integrity": "sha512-PSkFkr/w7UnFWm+EP8y/QpHrJXMqpZzAXpergB/EqLPOh4SGPJXv1wj4mslr2hUZBAS9pX7/9YLIdxTv6fwytw==",
-      "license": "MIT",
-      "dependencies": {
-        "@next/env": "15.0.1",
-        "@swc/counter": "0.1.3",
-        "@swc/helpers": "0.5.13",
-        "busboy": "1.6.0",
-        "caniuse-lite": "^1.0.30001579",
-        "postcss": "8.4.31",
-        "styled-jsx": "5.1.6"
-      },
-      "bin": {
-        "next": "dist/bin/next"
-      },
-      "engines": {
-        "node": ">=18.18.0"
-      },
-      "optionalDependencies": {
-        "@next/swc-darwin-arm64": "15.0.1",
-        "@next/swc-darwin-x64": "15.0.1",
-        "@next/swc-linux-arm64-gnu": "15.0.1",
-        "@next/swc-linux-arm64-musl": "15.0.1",
-        "@next/swc-linux-x64-gnu": "15.0.1",
-        "@next/swc-linux-x64-musl": "15.0.1",
-        "@next/swc-win32-arm64-msvc": "15.0.1",
-        "@next/swc-win32-x64-msvc": "15.0.1",
-        "sharp": "^0.33.5"
-      },
-      "peerDependencies": {
-        "@opentelemetry/api": "^1.1.0",
-        "@playwright/test": "^1.41.2",
-        "babel-plugin-react-compiler": "*",
-        "react": "^18.2.0 || 19.0.0-rc-69d4b800-20241021",
-        "react-dom": "^18.2.0 || 19.0.0-rc-69d4b800-20241021",
-        "sass": "^1.3.0"
-      },
-      "peerDependenciesMeta": {
-        "@opentelemetry/api": {
-          "optional": true
-        },
-        "@playwright/test": {
-          "optional": true
-        },
-        "babel-plugin-react-compiler": {
-          "optional": true
-        },
-        "sass": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/next/node_modules/postcss": {
-      "version": "8.4.31",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
-      "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
-      "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.6",
-        "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.2"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      }
-    },
-    "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/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==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/object-hash": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
-      "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/object-inspect": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
-      "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.assign": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
-      "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.5",
-        "define-properties": "^1.2.1",
-        "has-symbols": "^1.0.3",
-        "object-keys": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object.entries": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz",
-      "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.fromentries": {
-      "version": "2.0.8",
-      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
-      "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object.groupby": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
-      "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.values": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
-      "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "wrappy": "1"
-      }
-    },
-    "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/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-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/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/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-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "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==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "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/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": "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/pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/pirates": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
-      "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/possible-typed-array-names": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
-      "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/postcss": {
-      "version": "8.4.47",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
-      "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
-      "dev": true,
-      "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.7",
-        "picocolors": "^1.1.0",
-        "source-map-js": "^1.2.1"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      }
-    },
-    "node_modules/postcss-import": {
-      "version": "15.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
-      "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.0.0",
-        "read-cache": "^1.0.0",
-        "resolve": "^1.1.7"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.0.0"
-      }
-    },
-    "node_modules/postcss-js": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
-      "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "camelcase-css": "^2.0.1"
-      },
-      "engines": {
-        "node": "^12 || ^14 || >= 16"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      },
-      "peerDependencies": {
-        "postcss": "^8.4.21"
-      }
-    },
-    "node_modules/postcss-load-config": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
-      "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "lilconfig": "^3.0.0",
-        "yaml": "^2.3.4"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "postcss": ">=8.0.9",
-        "ts-node": ">=9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "postcss": {
-          "optional": true
-        },
-        "ts-node": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/postcss-load-config/node_modules/lilconfig": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
-      "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antonk52"
-      }
-    },
-    "node_modules/postcss-nested": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
-      "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "postcss-selector-parser": "^6.1.1"
-      },
-      "engines": {
-        "node": ">=12.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.14"
-      }
-    },
-    "node_modules/postcss-selector-parser": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
-      "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
-      "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/prop-types": {
-      "version": "15.8.1",
-      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
-      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.4.0",
-        "object-assign": "^4.1.1",
-        "react-is": "^16.13.1"
-      }
-    },
-    "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/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/react": {
-      "version": "19.0.0-rc-69d4b800-20241021",
-      "resolved": "https://registry.npmjs.org/react/-/react-19.0.0-rc-69d4b800-20241021.tgz",
-      "integrity": "sha512-dXki4tN+rP+4xhsm65q/QI/19VCZdu5vPcy4h6zaJt20XP8/1r/LCwrLFYuj8hElbNz5AmxW6JtRa7ej0BzZdg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/react-dom": {
-      "version": "19.0.0-rc-69d4b800-20241021",
-      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0-rc-69d4b800-20241021.tgz",
-      "integrity": "sha512-ZXBsP/kTDLI9QopUaUgYJhmmAhO8aKz7DCv2Ui2rA9boCfJ/dRRh6BlVidsyb2dPzG01rItdRFQqwbP+x9s5Rg==",
-      "license": "MIT",
-      "dependencies": {
-        "scheduler": "0.25.0-rc-69d4b800-20241021"
-      },
-      "peerDependencies": {
-        "react": "19.0.0-rc-69d4b800-20241021"
-      }
-    },
-    "node_modules/react-is": {
-      "version": "16.13.1",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
-      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/read-cache": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
-      "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "pify": "^2.3.0"
-      }
-    },
-    "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/reflect.getprototypeof": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
-      "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.1",
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4",
-        "globalthis": "^1.0.3",
-        "which-builtin-type": "^1.1.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/regexp.prototype.flags": {
-      "version": "1.5.3",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
-      "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-errors": "^1.3.0",
-        "set-function-name": "^2.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/resolve": {
-      "version": "1.22.8",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
-      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-core-module": "^2.13.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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/resolve-pkg-maps": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
-      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
-      }
-    },
-    "node_modules/reusify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "iojs": ">=1.0.0",
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
-      "deprecated": "Rimraf versions prior to v4 are no longer supported",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "glob": "^7.1.3"
-      },
-      "bin": {
-        "rimraf": "bin.js"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "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/safe-array-concat": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
-      "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "get-intrinsic": "^1.2.4",
-        "has-symbols": "^1.0.3",
-        "isarray": "^2.0.5"
-      },
-      "engines": {
-        "node": ">=0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/safe-regex-test": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
-      "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.6",
-        "es-errors": "^1.3.0",
-        "is-regex": "^1.1.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/scheduler": {
-      "version": "0.25.0-rc-69d4b800-20241021",
-      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-69d4b800-20241021.tgz",
-      "integrity": "sha512-S5AYX/YhMAN6u9AXgKYbZP4U4ZklC6R9Q7HmFSBk7d4DLiHVNxvAvlSvuM4nxFkwOk50MnpfTKQ7UWHXDOc9Eg==",
-      "license": "MIT"
-    },
-    "node_modules/semver": {
-      "version": "7.6.3",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
-      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
-      "devOptional": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/set-function-length": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
-      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/set-function-name": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
-      "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "functions-have-names": "^1.2.3",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/sharp": {
-      "version": "0.33.5",
-      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
-      "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
-      "hasInstallScript": true,
-      "license": "Apache-2.0",
-      "optional": true,
-      "dependencies": {
-        "color": "^4.2.3",
-        "detect-libc": "^2.0.3",
-        "semver": "^7.6.3"
-      },
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-darwin-arm64": "0.33.5",
-        "@img/sharp-darwin-x64": "0.33.5",
-        "@img/sharp-libvips-darwin-arm64": "1.0.4",
-        "@img/sharp-libvips-darwin-x64": "1.0.4",
-        "@img/sharp-libvips-linux-arm": "1.0.5",
-        "@img/sharp-libvips-linux-arm64": "1.0.4",
-        "@img/sharp-libvips-linux-s390x": "1.0.4",
-        "@img/sharp-libvips-linux-x64": "1.0.4",
-        "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
-        "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
-        "@img/sharp-linux-arm": "0.33.5",
-        "@img/sharp-linux-arm64": "0.33.5",
-        "@img/sharp-linux-s390x": "0.33.5",
-        "@img/sharp-linux-x64": "0.33.5",
-        "@img/sharp-linuxmusl-arm64": "0.33.5",
-        "@img/sharp-linuxmusl-x64": "0.33.5",
-        "@img/sharp-wasm32": "0.33.5",
-        "@img/sharp-win32-ia32": "0.33.5",
-        "@img/sharp-win32-x64": "0.33.5"
-      }
-    },
-    "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==",
-      "dev": true,
-      "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==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/side-channel": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
-      "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4",
-        "object-inspect": "^1.13.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/simple-swizzle": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
-      "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
-      "license": "MIT",
-      "optional": true,
-      "dependencies": {
-        "is-arrayish": "^0.3.1"
-      }
-    },
-    "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/streamsearch": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
-      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/string-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/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/string-width-cjs/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==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/string-width/node_modules/ansi-regex": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
-      "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
-      }
-    },
-    "node_modules/string-width/node_modules/strip-ansi": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
-      "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
-      "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/string.prototype.includes": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
-      "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/string.prototype.matchall": {
-      "version": "4.0.11",
-      "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz",
-      "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-symbols": "^1.0.3",
-        "internal-slot": "^1.0.7",
-        "regexp.prototype.flags": "^1.5.2",
-        "set-function-name": "^2.0.2",
-        "side-channel": "^1.0.6"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/string.prototype.repeat": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
-      "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.17.5"
-      }
-    },
-    "node_modules/string.prototype.trim": {
-      "version": "1.2.9",
-      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
-      "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.0",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/string.prototype.trimend": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
-      "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/string.prototype.trimstart": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
-      "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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==",
-      "dev": true,
-      "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-bom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "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/styled-jsx": {
-      "version": "5.1.6",
-      "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
-      "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
-      "license": "MIT",
-      "dependencies": {
-        "client-only": "0.0.1"
-      },
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "peerDependencies": {
-        "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
-      },
-      "peerDependenciesMeta": {
-        "@babel/core": {
-          "optional": true
-        },
-        "babel-plugin-macros": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/sucrase": {
-      "version": "3.35.0",
-      "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
-      "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "commander": "^4.0.0",
-        "glob": "^10.3.10",
-        "lines-and-columns": "^1.1.6",
-        "mz": "^2.7.0",
-        "pirates": "^4.0.1",
-        "ts-interface-checker": "^0.1.9"
-      },
-      "bin": {
-        "sucrase": "bin/sucrase",
-        "sucrase-node": "bin/sucrase-node"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      }
-    },
-    "node_modules/sucrase/node_modules/brace-expansion": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
-    "node_modules/sucrase/node_modules/glob": {
-      "version": "10.4.5",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
-      "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
-      "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/sucrase/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/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/supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/tailwindcss": {
-      "version": "3.4.14",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz",
-      "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@alloc/quick-lru": "^5.2.0",
-        "arg": "^5.0.2",
-        "chokidar": "^3.5.3",
-        "didyoumean": "^1.2.2",
-        "dlv": "^1.1.3",
-        "fast-glob": "^3.3.0",
-        "glob-parent": "^6.0.2",
-        "is-glob": "^4.0.3",
-        "jiti": "^1.21.0",
-        "lilconfig": "^2.1.0",
-        "micromatch": "^4.0.5",
-        "normalize-path": "^3.0.0",
-        "object-hash": "^3.0.0",
-        "picocolors": "^1.0.0",
-        "postcss": "^8.4.23",
-        "postcss-import": "^15.1.0",
-        "postcss-js": "^4.0.1",
-        "postcss-load-config": "^4.0.1",
-        "postcss-nested": "^6.0.1",
-        "postcss-selector-parser": "^6.0.11",
-        "resolve": "^1.22.2",
-        "sucrase": "^3.32.0"
-      },
-      "bin": {
-        "tailwind": "lib/cli.js",
-        "tailwindcss": "lib/cli.js"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      }
-    },
-    "node_modules/tapable": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
-      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/thenify": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
-      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "any-promise": "^1.0.0"
-      }
-    },
-    "node_modules/thenify-all": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
-      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "thenify": ">= 3.1.0 < 4"
-      },
-      "engines": {
-        "node": ">=0.8"
-      }
-    },
-    "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==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-number": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=8.0"
-      }
-    },
-    "node_modules/ts-api-utils": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
-      "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=16"
-      },
-      "peerDependencies": {
-        "typescript": ">=4.2.0"
-      }
-    },
-    "node_modules/ts-interface-checker": {
-      "version": "0.1.13",
-      "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
-      "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
-      "dev": true,
-      "license": "Apache-2.0"
-    },
-    "node_modules/tsconfig-paths": {
-      "version": "3.15.0",
-      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
-      "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json5": "^0.0.29",
-        "json5": "^1.0.2",
-        "minimist": "^1.2.6",
-        "strip-bom": "^3.0.0"
-      }
-    },
-    "node_modules/tslib": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
-      "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==",
-      "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": "0.20.2",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
-      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/typed-array-buffer": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
-      "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "es-errors": "^1.3.0",
-        "is-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/typed-array-byte-length": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
-      "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "has-proto": "^1.0.3",
-        "is-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/typed-array-byte-offset": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
-      "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "available-typed-arrays": "^1.0.7",
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "has-proto": "^1.0.3",
-        "is-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/typed-array-length": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
-      "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "has-proto": "^1.0.3",
-        "is-typed-array": "^1.1.13",
-        "possible-typed-array-names": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/typescript": {
-      "version": "5.6.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
-      "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
-      "engines": {
-        "node": ">=14.17"
-      }
-    },
-    "node_modules/unbox-primitive": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
-      "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-bigints": "^1.0.2",
-        "has-symbols": "^1.0.3",
-        "which-boxed-primitive": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/undici-types": {
-      "version": "6.19.8",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
-      "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "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/which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/which-boxed-primitive": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
-      "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-bigint": "^1.0.1",
-        "is-boolean-object": "^1.1.0",
-        "is-number-object": "^1.0.4",
-        "is-string": "^1.0.5",
-        "is-symbol": "^1.0.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/which-builtin-type": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz",
-      "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "function.prototype.name": "^1.1.6",
-        "has-tostringtag": "^1.0.2",
-        "is-async-function": "^2.0.0",
-        "is-date-object": "^1.0.5",
-        "is-finalizationregistry": "^1.0.2",
-        "is-generator-function": "^1.0.10",
-        "is-regex": "^1.1.4",
-        "is-weakref": "^1.0.2",
-        "isarray": "^2.0.5",
-        "which-boxed-primitive": "^1.0.2",
-        "which-collection": "^1.0.2",
-        "which-typed-array": "^1.1.15"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/which-collection": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
-      "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-map": "^2.0.3",
-        "is-set": "^2.0.3",
-        "is-weakmap": "^2.0.2",
-        "is-weakset": "^2.0.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/which-typed-array": {
-      "version": "1.1.15",
-      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
-      "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "available-typed-arrays": "^1.0.7",
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "has-tostringtag": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "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": "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/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/wrap-ansi-cjs/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==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/wrap-ansi-cjs/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==",
-      "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/wrap-ansi/node_modules/ansi-regex": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
-      "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
-      }
-    },
-    "node_modules/wrap-ansi/node_modules/ansi-styles": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
-      "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/wrap-ansi/node_modules/strip-ansi": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
-      "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
-      "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/wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/yaml": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz",
-      "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "yaml": "bin.mjs"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "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"
-      }
-    }
-  }
-}
diff --git a/src/feature/common/failures/failure-helpers.ts b/src/feature/common/failures/failure-helpers.ts
index a6c8f46..673b8aa 100644
--- a/src/feature/common/failures/failure-helpers.ts
+++ b/src/feature/common/failures/failure-helpers.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
 import BaseFailure from "@/feature/common/failures/base-failure";
 
 /**
@@ -16,16 +17,19 @@ import BaseFailure from "@/feature/common/failures/base-failure";
  * )
  * ```
  * In this example `failureOr` will return already throwed
- * instance of `BaseFailure` which is `ValidationFailure`.
+ * instance of `BaseFailure<any>` which is `ValidationFailure`.
  *
  *
  * @param reason is throwed object.
- * Basically it can be default `Error` or instance of `BaseFailure`.
- * @param failure instance of `BaseFailure` that will be returned
- * if reason is not instance of `BaseFailure`.
- * @returns `BaseFailure`
+ * Basically it can be default `Error` or instance of `BaseFailure<any>`.
+ * @param failure instance of `BaseFailure<any>` that will be returned
+ * if reason is not instance of `BaseFailure<any>`.
+ * @returns `BaseFailure<any>`
  */
-export function failureOr(reason: unknown, failure: BaseFailure): BaseFailure {
+export function failureOr(
+  reason: unknown,
+  failure: BaseFailure<any>,
+): BaseFailure<any> {
   if (reason instanceof BaseFailure) {
     return reason;
   }
@@ -33,16 +37,16 @@ export function failureOr(reason: unknown, failure: BaseFailure): BaseFailure {
 }
 
 /**
- * Returns a function that maps a BaseFailure instance to a new BaseFailure instance of type IfType using the provided mapping function.
- * @param f A function that maps an instance of IfType to a new instance of BaseFailure.
+ * Returns a function that maps a BaseFailure<any> instance to a new BaseFailure<any> instance of type IfType using the provided mapping function.
+ * @param f A function that maps an instance of IfType to a new instance of BaseFailure<any>.
  * @param ctor A constructor function for IfType.
- * @returns A function that maps a BaseFailure instance to a new BaseFailure instance of type IfType.
+ * @returns A function that maps a BaseFailure<any> instance to a new BaseFailure<any> instance of type IfType.
  */
-export function mapToFailureFrom<IfType extends BaseFailure>(
-  f: (t: IfType) => BaseFailure,
+export function mapToFailureFrom<IfType extends BaseFailure<any>>(
+  f: (t: IfType) => BaseFailure<any>,
   ctor: new (...args: never[]) => IfType,
-): (t: BaseFailure) => BaseFailure {
-  return mapIfInstance<IfType, BaseFailure>(f, ctor);
+): (t: BaseFailure<any>) => BaseFailure<any> {
+  return mapIfInstance<IfType, BaseFailure<any>>(f, ctor);
 }
 
 /**
-- 
2.39.5


From 1675d84caee45c51460ef63de46a5d902d0deb2e Mon Sep 17 00:00:00 2001
From: behnam <behnamrahimpour74@gmail.com>
Date: Tue, 26 Nov 2024 15:42:16 +0000
Subject: [PATCH 37/37] Add storybook with i18n and darkmode

Reviewed-on: https://git.dipal.ru/behnam/Nextjs-boilerplate/pulls/2
---
 .eslintignore                                 |    1 +
 .eslintrc.json                                |   48 +-
 .gitignore                                    |    2 +
 .storybook/main.ts                            |   18 +
 .storybook/preview.tsx                        |  134 +
 docker-compose.yml                            |    1 -
 package.json                                  |   16 +-
 .../client/theme-provider/theme-provider.tsx  |   11 +
 src/app/[lang]/dashboard/page.tsx             |    4 +-
 src/app/[lang]/layout.tsx                     |   20 +-
 src/app/components/button/button.tsx          |    1 -
 .../button/stories/Button.stories.tsx         |   65 +
 src/app/globals.css                           |    2 +-
 src/bootstrap/di/mocked-module-di.ts          |   33 +
 src/bootstrap/helpers/global-helpers.ts       |    7 +
 src/bootstrap/helpers/hooks/use-throttle.ts   |    4 +-
 .../view/storybook-base-template-type.ts      |    5 +
 .../helpers/view/storybook-with-arg-vm.ts     |   14 +
 src/bootstrap/i18n/i18n-provider.tsx          |   13 +-
 src/bootstrap/i18n/i18n.ts                    |   26 +-
 tailwind.config.ts                            |  102 +-
 yarn.lock                                     | 3643 ++++++++++++++++-
 22 files changed, 4015 insertions(+), 155 deletions(-)
 create mode 100644 .eslintignore
 create mode 100644 .storybook/main.ts
 create mode 100644 .storybook/preview.tsx
 create mode 100644 src/app/[lang]/dashboard/components/client/theme-provider/theme-provider.tsx
 create mode 100644 src/app/components/button/stories/Button.stories.tsx
 create mode 100644 src/bootstrap/di/mocked-module-di.ts
 create mode 100644 src/bootstrap/helpers/view/storybook-base-template-type.ts
 create mode 100644 src/bootstrap/helpers/view/storybook-with-arg-vm.ts

diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..9e6de2f
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+tailwind.config.ts
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
index 6307415..1c88a7d 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -5,14 +5,19 @@
         "src/**/*-vm.ts"
       ],
       "rules": {
-          "react-hooks/rules-of-hooks": "off"
+        "react-hooks/rules-of-hooks": "off"
       }
+    },
+    {
+      "files": [
+        "src/app/**/*.stories.tsx"
+      ],
+      "rules": {
+        "react/jsx-props-no-spreading": "off"
+      }      
     }
   ],
-  "plugins": [
-    "prettier"
-  ],
-  "settings": {
+    "settings": {
     "react": {
       "version": "detect"
     },
@@ -35,6 +40,16 @@
       }
     }
   },
+  "plugins": [
+    "prettier"
+  ],
+  "extends": [
+    "airbnb",
+    "next/core-web-vitals",
+    "next/typescript",
+    "prettier",
+    "plugin:storybook/recommended"
+  ],
   "rules": {
     "no-use-before-define": "off",
     "class-methods-use-this": "off",
@@ -43,13 +58,24 @@
     "no-promise-executor-return": "off",
     "@typescript-eslint/no-shadow": "off",
     "react/require-default-props": "off",
+    "import/order": [
+      "error",
+      {
+        "pathGroups": [
+          {
+            "pattern": "@/**",
+            "group": "external"
+          }
+        ]
+      } 
+    ],
     "no-shadow": "off",
     "prettier/prettier": [
       "warn",
       {
         "printWidth": 80,
         "tabWidth": 2,
-        "endOfLine":"auto",
+        "endOfLine": "auto",
         "useTabs": false,
         "semi": true,
         "singleQuote": false,
@@ -85,11 +111,5 @@
         "devDependencies": true
       }
     ]
-  },
-  "extends": [
-    "airbnb",
-    "next/core-web-vitals", 
-    "next/typescript",
-    "prettier"
-  ]
-}
+  }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 39c19d2..a39f2a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,5 @@ yarn-error.log*
 # typescript
 *.tsbuildinfo
 next-env.d.ts
+
+*storybook.log
diff --git a/.storybook/main.ts b/.storybook/main.ts
new file mode 100644
index 0000000..6efa0cd
--- /dev/null
+++ b/.storybook/main.ts
@@ -0,0 +1,18 @@
+import type { StorybookConfig } from "@storybook/nextjs";
+
+const config: StorybookConfig = {
+  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
+  addons: [
+    "@storybook/addon-onboarding",
+    "@storybook/addon-essentials",
+    "@chromatic-com/storybook",
+    "@storybook/addon-interactions",
+    "storybook-dark-mode"
+  ],
+  framework: {
+    name: "@storybook/nextjs",
+    options: {},
+  },
+  staticDirs: ["../public"],
+};
+export default config;
diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
new file mode 100644
index 0000000..dc1dbaa
--- /dev/null
+++ b/.storybook/preview.tsx
@@ -0,0 +1,134 @@
+import React, { useRef } from "react";
+import { themes } from '@storybook/theming';
+import { ThemeProvider } from "../src/app/[lang]/dashboard/components/client/theme-provider/theme-provider";
+import { DARK_MODE_EVENT_NAME, UPDATE_DARK_MODE_EVENT_NAME } from 'storybook-dark-mode';
+import { initI18next, LANGS } from "../src/bootstrap/i18n/i18n"
+import { addons } from '@storybook/preview-api';
+import { i18n } from "i18next";
+import { I18nextProvider } from "react-i18next";
+const channel = addons.getChannel();
+import "../src/app/globals.css"
+/**
+ *
+ * This function will expand the object with nested properties
+ * @param obj refrence Object to that
+ * @param leftKeys keys to be nested object keys
+ * @param value value to be nested
+ *
+ */
+export const recursiveNestedProps = (
+  obj: Record<string, unknown>,
+  leftKeys: string[],
+  value: unknown,
+): Record<string, unknown> => {
+  if (leftKeys.length <= 0) return obj;
+  if (leftKeys.length === 1) {
+    // eslint-disable-next-line no-param-reassign
+    obj[leftKeys[0]] = value;
+    return obj;
+  }
+  const key = leftKeys.shift();
+  if (!key) return obj;
+
+  if (!obj[key]) {
+    // eslint-disable-next-line no-param-reassign
+    obj[key] = {};
+  }
+
+  return recursiveNestedProps(
+    obj[key] as Record<string, unknown>,
+    leftKeys,
+    value,
+  );
+};
+
+const preview = {
+    decorators: [
+    (Story, data) => {
+      const [isDark, setDark] = React.useState(true);
+      const [i18n, setI18n] = React.useState<i18n>()
+      const parsedProps = {} as Record<string, unknown>;
+      const { locale } = data.globals
+      const props = data.allArgs;
+      Object.entries(props).forEach((prop) => {
+        const [key, value] = prop;
+        if (!key.includes("vm")) {
+          parsedProps[key] = value;
+          return;
+        }
+        const splitedKey = key.split(".");
+
+        recursiveNestedProps(parsedProps, splitedKey, value);
+      });
+
+
+      React.useEffect(() => {
+        channel.on(DARK_MODE_EVENT_NAME, setDark);
+        return () => channel.removeListener(DARK_MODE_EVENT_NAME, setDark);
+      }, [channel, setDark]);
+
+      React.useEffect(() => {
+        (async () => {
+          setI18n((await initI18next({ lng: locale })).i18n);
+        })()
+      }, [])
+
+      React.useEffect(() => {
+          i18n?.changeLanguage(locale);
+      }, [locale]);
+
+      return (
+        <ThemeProvider
+          attribute="class"
+          forcedTheme={isDark ? "dark" : "light"}
+          enableSystem
+          disableTransitionOnChange
+       >
+        {
+          i18n && (
+            <I18nextProvider 
+              i18n={i18n}
+              >
+              <Story parsedProps={parsedProps} />
+            </I18nextProvider>
+          )
+        }
+       </ThemeProvider> 
+      );
+    },
+  ],
+  darkMode: {
+    // Override the default dark theme
+    dark: { ...themes.dark, appBg: 'black' },
+    // Override the default light theme
+    classTarget: 'html',
+    light: { ...themes.normal, appBg: 'red' },
+  },
+  parameters: {
+    nextjs: {
+      appDirectory: true,
+    },
+    controls: {
+      matchers: {
+        color: /(background|color)$/i,
+        date: /Date$/i,
+      },
+    },
+  },
+  globalTypes: {
+    locale: {
+      name: 'Locale',
+      description: 'Internationalization locale',
+      toolbar: {
+        icon: 'globe',
+        items: [
+          { value: LANGS.EN, title: 'English' },
+          { value: LANGS.RU, title: 'Russian' },
+        ],
+        showName: true,
+      },
+    },
+  }
+};
+
+export default preview;
diff --git a/docker-compose.yml b/docker-compose.yml
index 9e2981f..189a656 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,7 +11,6 @@ services:
       POSTGRES_USER: admin
       POSTGRES_DB: nextbp 
 
-
   app:
     build: .
     ports:
diff --git a/package.json b/package.json
index ad7a6a0..59fef4e 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,9 @@
     "start": "next start --port 4000",
     "lint": "next lint --fix",
     "test": "vitest",
-    "seed": "node -r dotenv/config ./src/bootstrap/boundaries/db/seed.js"
+    "seed": "node -r dotenv/config ./src/bootstrap/boundaries/db/seed.js",
+    "storybook": "storybook dev -p 6006 --no-open",
+    "build-storybook": "storybook build"
   },
   "dependencies": {
     "@heroicons/react": "^2.1.5",
@@ -24,6 +26,7 @@
     "lucide-react": "^0.454.0",
     "next": "15.0.2",
     "next-i18n-router": "^5.5.1",
+    "next-themes": "^0.4.3",
     "postgres": "^3.4.5",
     "react": "19.0.0-rc-69d4b800-20241021",
     "react-cookie": "^7.2.2",
@@ -37,7 +40,15 @@
     "zod": "^3.23.8"
   },
   "devDependencies": {
+    "@chromatic-com/storybook": "^3.2.2",
     "@faker-js/faker": "^9.1.0",
+    "@storybook/addon-essentials": "^8.4.5",
+    "@storybook/addon-interactions": "^8.4.5",
+    "@storybook/addon-onboarding": "^8.4.5",
+    "@storybook/blocks": "^8.4.5",
+    "@storybook/nextjs": "^8.4.5",
+    "@storybook/react": "^8.4.5",
+    "@storybook/test": "^8.4.5",
     "@testing-library/dom": "^10.4.0",
     "@testing-library/react": "^16.0.1",
     "@types/node": "^20",
@@ -53,10 +64,13 @@
     "eslint-import-resolver-alias": "^1.1.2",
     "eslint-import-resolver-typescript": "^3.6.3",
     "eslint-plugin-prettier": "^5.2.1",
+    "eslint-plugin-storybook": "^0.11.1",
     "jsdom": "^25.0.1",
     "moq.ts": "^10.0.8",
     "postcss": "^8",
     "prettier": "^3.3.3",
+    "storybook": "^8.4.5",
+    "storybook-dark-mode": "^4.0.2",
     "tailwindcss": "^3.4.1",
     "typescript": "^5",
     "vitest": "^2.1.4"
diff --git a/src/app/[lang]/dashboard/components/client/theme-provider/theme-provider.tsx b/src/app/[lang]/dashboard/components/client/theme-provider/theme-provider.tsx
new file mode 100644
index 0000000..6a1ffe4
--- /dev/null
+++ b/src/app/[lang]/dashboard/components/client/theme-provider/theme-provider.tsx
@@ -0,0 +1,11 @@
+"use client"
+
+import * as React from "react"
+import { ThemeProvider as NextThemesProvider } from "next-themes"
+
+export function ThemeProvider({
+  children,
+  ...props
+}: React.ComponentProps<typeof NextThemesProvider>) {
+  return <NextThemesProvider {...props}>{children}</NextThemesProvider>
+}
diff --git a/src/app/[lang]/dashboard/page.tsx b/src/app/[lang]/dashboard/page.tsx
index f9f980f..ea09ada 100644
--- a/src/app/[lang]/dashboard/page.tsx
+++ b/src/app/[lang]/dashboard/page.tsx
@@ -6,11 +6,11 @@ import CardWrapper from "@/app/[lang]/dashboard/components/server/cards/cards";
 import LatestInvoices from "@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices";
 import RevenueChart from "@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart";
 import { Suspense } from "react";
-import { getServerTranslation } from "@/bootstrap/i18n/i18n";
+import { getServerTranslation, LANGS } from "@/bootstrap/i18n/i18n";
 import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
 
 export default async function Dashboard(props: {
-  params: Promise<{ lang: string }>;
+  params: Promise<{ lang: LANGS }>;
 }) {
   const { params } = props;
   const { lang } = await params;
diff --git a/src/app/[lang]/layout.tsx b/src/app/[lang]/layout.tsx
index b142672..89037c1 100644
--- a/src/app/[lang]/layout.tsx
+++ b/src/app/[lang]/layout.tsx
@@ -1,4 +1,5 @@
-import { initI18next } from "@/bootstrap/i18n/i18n";
+import { ThemeProvider } from "@/app/[lang]/dashboard/components/client/theme-provider/theme-provider";
+import { initI18next, LANGS } from "@/bootstrap/i18n/i18n";
 import TranslationsProvider from "@/bootstrap/i18n/i18n-provider";
 import localFont from "next/font/local";
 import { PropsWithChildren } from "react";
@@ -15,19 +16,26 @@ const geistMono = localFont({
 });
 
 export default async function layout(
-  props: PropsWithChildren & { params: Promise<{ lang: string }> },
+  props: PropsWithChildren & { params: Promise<{ lang: LANGS }> },
 ) {
   const { params, children } = props;
   const { lang } = await params;
   const { resources } = await initI18next({ lng: lang });
   return (
-    <html lang={lang}>
+    <html lang={lang} suppressHydrationWarning>
       <body
         className={`${geistSans.variable} ${geistMono.variable} antialiased`}
       >
-        <TranslationsProvider lng={lang} resources={resources}>
-          {children}
-        </TranslationsProvider>
+        <ThemeProvider
+          attribute="class"
+          defaultTheme="light"
+          enableSystem
+          disableTransitionOnChange
+        >
+          <TranslationsProvider lng={lang} resources={resources}>
+            {children}
+          </TranslationsProvider>
+        </ThemeProvider>
       </body>
     </html>
   );
diff --git a/src/app/components/button/button.tsx b/src/app/components/button/button.tsx
index 5520728..2c72f27 100644
--- a/src/app/components/button/button.tsx
+++ b/src/app/components/button/button.tsx
@@ -11,7 +11,6 @@ import { cn } from "@/bootstrap/helpers/lib/ui-utils";
 export default class Button extends BaseView<ButtonVm> {
   protected Build(props: BuildProps<ButtonVm>): ReactNode {
     const { vm } = props;
-
     return (
       <ButtonUi disabled={vm.props.isDisable} onClick={vm.onClick}>
         {vm.props.title}
diff --git a/src/app/components/button/stories/Button.stories.tsx b/src/app/components/button/stories/Button.stories.tsx
new file mode 100644
index 0000000..d7739ae
--- /dev/null
+++ b/src/app/components/button/stories/Button.stories.tsx
@@ -0,0 +1,65 @@
+import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm";
+import Button from "@/app/components/button/button";
+import { DiContext, useDI } from "@/bootstrap/di/di-context";
+import mockedModuleDi from "@/bootstrap/di/mocked-module-di";
+import Story from "@/bootstrap/helpers/view/storybook-base-template-type";
+import getArgVM from "@/bootstrap/helpers/view/storybook-with-arg-vm";
+import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
+import type { Meta } from "@storybook/react";
+import { useRef } from "react";
+
+const meta: Meta = {
+  title: "general/Button",
+};
+
+export default meta;
+
+export const Primary: Story = {
+  argTypes: {
+    "vm.props.isDisable": {
+      control: "boolean",
+    },
+    "vm.props.title": {
+      control: "text",
+    },
+  },
+  args: {
+    "vm.props.title": "Button",
+    "vm.props.isDisable": false,
+  },
+  render: (_props, globalData) => {
+    const vm = getArgVM(globalData.parsedProps.vm); // You can use parsed props to access your vm properties.
+    return <Button vm={vm} memoizedByVM={false} />;
+  },
+};
+
+export const WithVM: Story = {
+  decorators: [
+    (Story) => {
+      const di = mockedModuleDi([
+        {
+          token: CreateRandomInvoiceButtonVM,
+          provider: CreateRandomInvoiceButtonVM,
+        },
+        {
+          token: createInvoiceUsecase.name,
+          // eslint-disable-next-line @typescript-eslint/no-explicit-any, no-console
+          provider: (args: any) => console.log("clicked", args),
+        },
+      ]);
+      return <Story di={di} />;
+    },
+  ],
+  render: (_, globalProps) => {
+    function Child() {
+      const di = useDI();
+      const vm = useRef(di.resolve(CreateRandomInvoiceButtonVM));
+      return <Button vm={vm.current} memoizedByVM={false} />;
+    }
+    return (
+      <DiContext.Provider value={globalProps.di}>
+        <Child />
+      </DiContext.Provider>
+    );
+  },
+};
diff --git a/src/app/globals.css b/src/app/globals.css
index 942f871..be56f49 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -34,7 +34,7 @@ body {
     --chart-5: 27 87% 67%;
     --radius: 0.5rem;
   }
-  .dark {
+  :root[class~="dark"] {
     --background: 240 10% 3.9%;
     --foreground: 0 0% 98%;
     --card: 240 10% 3.9%;
diff --git a/src/bootstrap/di/mocked-module-di.ts b/src/bootstrap/di/mocked-module-di.ts
new file mode 100644
index 0000000..5fc1d93
--- /dev/null
+++ b/src/bootstrap/di/mocked-module-di.ts
@@ -0,0 +1,33 @@
+import { container, DependencyContainer } from "tsyringe";
+import { InjectionToken } from "tsyringe/dist/typings/providers";
+import constructor from "tsyringe/dist/typings/types/constructor";
+import { isClass } from "../helpers/global-helpers";
+
+/**
+ * Provides mocked di for test cases and using instead of real di
+ * @param providers List of providers
+ * @returns Mocked di with registered providers
+ */
+const mockedModuleDi = (
+  providers: {
+    token: InjectionToken<unknown>;
+    provider: unknown | constructor<unknown>;
+  }[],
+): DependencyContainer => {
+  const di = container.createChildContainer();
+
+  providers.forEach((provider) => {
+    if (isClass(provider.provider)) {
+      di.register(provider.token, provider.provider);
+      return;
+    }
+
+    di.register(provider.token, {
+      useValue: provider.provider,
+    });
+  });
+
+  return di;
+};
+
+export default mockedModuleDi;
diff --git a/src/bootstrap/helpers/global-helpers.ts b/src/bootstrap/helpers/global-helpers.ts
index ca617a8..ca009a2 100644
--- a/src/bootstrap/helpers/global-helpers.ts
+++ b/src/bootstrap/helpers/global-helpers.ts
@@ -1 +1,8 @@
+import { constructor } from "tsyringe/dist/typings/types";
+
 export const isServer = typeof window === "undefined";
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function isClass(fn: any): fn is constructor<unknown> {
+  return typeof fn === "function" && /^(class|function [A-Z])/.test(fn);
+}
diff --git a/src/bootstrap/helpers/hooks/use-throttle.ts b/src/bootstrap/helpers/hooks/use-throttle.ts
index 6f8213b..b791f67 100644
--- a/src/bootstrap/helpers/hooks/use-throttle.ts
+++ b/src/bootstrap/helpers/hooks/use-throttle.ts
@@ -11,11 +11,11 @@ export default function useThrottle<T extends () => unknown>(
   callback: T,
   time: number = 2000,
 ) {
-  const lastRun = useRef(Date.now());
+  const lastRun = useRef<number>();
 
   // eslint-disable-next-line func-names
   return function () {
-    if (Date.now() - lastRun.current <= time) return;
+    if (lastRun.current && Date.now() - lastRun.current <= time) return;
     lastRun.current = Date.now();
     callback();
   };
diff --git a/src/bootstrap/helpers/view/storybook-base-template-type.ts b/src/bootstrap/helpers/view/storybook-base-template-type.ts
new file mode 100644
index 0000000..763c5fe
--- /dev/null
+++ b/src/bootstrap/helpers/view/storybook-base-template-type.ts
@@ -0,0 +1,5 @@
+import { Meta, StoryObj } from "@storybook/react";
+
+type Story = StoryObj<Meta>;
+
+export default Story;
diff --git a/src/bootstrap/helpers/view/storybook-with-arg-vm.ts b/src/bootstrap/helpers/view/storybook-with-arg-vm.ts
new file mode 100644
index 0000000..210e173
--- /dev/null
+++ b/src/bootstrap/helpers/view/storybook-with-arg-vm.ts
@@ -0,0 +1,14 @@
+import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
+
+const getArgVM = <IVM>(vmObj: IVM) => {
+  class VM implements IBaseVM<IVM> {
+    useVM(): IVM {
+      return {
+        ...vmObj,
+      };
+    }
+  }
+  return new VM();
+};
+
+export default getArgVM;
diff --git a/src/bootstrap/i18n/i18n-provider.tsx b/src/bootstrap/i18n/i18n-provider.tsx
index c3272d8..ed50a29 100644
--- a/src/bootstrap/i18n/i18n-provider.tsx
+++ b/src/bootstrap/i18n/i18n-provider.tsx
@@ -1,18 +1,17 @@
 "use client";
 
 import { I18nextProvider } from "react-i18next";
-import { initI18next } from "@/bootstrap/i18n/i18n";
-import { createInstance, Resource } from "i18next";
+import { i18nInstance, initI18next, LANGS } from "@/bootstrap/i18n/i18n";
+import { Resource } from "i18next";
 import { PropsWithChildren } from "react";
 
 export default function TranslationsProvider({
   children,
   lng,
   resources,
-}: PropsWithChildren & { lng: string; resources: Resource }) {
-  const i18n = createInstance();
+}: PropsWithChildren & { lng: LANGS; resources: Resource }) {
+  if (!resources) return children;
+  initI18next({ lng, resources });
 
-  initI18next({ lng, i18n, resources });
-
-  return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
+  return <I18nextProvider i18n={i18nInstance}>{children}</I18nextProvider>;
 }
diff --git a/src/bootstrap/i18n/i18n.ts b/src/bootstrap/i18n/i18n.ts
index c95d601..9e376a9 100644
--- a/src/bootstrap/i18n/i18n.ts
+++ b/src/bootstrap/i18n/i18n.ts
@@ -1,23 +1,26 @@
 import { getOptions, languages } from "@/bootstrap/i18n/settings";
-import { createInstance, i18n, Resource } from "i18next";
+import { createInstance, Resource } from "i18next";
 import resourcesToBackend from "i18next-resources-to-backend";
 import { initReactI18next } from "react-i18next/initReactI18next";
 
-const initI18nextInstance = createInstance();
+export const i18nInstance = createInstance();
+
+export enum LANGS {
+  EN = "en",
+  RU = "ru",
+}
 
 export const initI18next = async (params: {
-  lng: string;
-  i18n?: i18n;
+  lng: LANGS;
   resources?: Resource;
   ns?: string;
 }) => {
-  const { lng, i18n, ns, resources } = params;
-  const i18nInstance = i18n || initI18nextInstance;
+  const { lng, ns, resources } = params;
   await i18nInstance
     .use(initReactI18next)
     .use(
       resourcesToBackend(
-        (language: string) => import(`./dictionaries/${language}.ts`),
+        (language: LANGS) => import(`./dictionaries/${language}.ts`),
       ),
     )
     .init({
@@ -36,18 +39,17 @@ export const initI18next = async (params: {
 };
 
 export async function getServerTranslation(
-  lng: string,
+  lng: LANGS,
   ns?: string,
   options: { keyPrefix?: string } = {},
 ) {
-  const i18nextInstance = (await initI18next({ lng, ns })).i18n;
-
+  await initI18next({ lng });
   return {
-    t: i18nextInstance.getFixedT(
+    t: i18nInstance.getFixedT(
       lng,
       Array.isArray(ns) ? ns[0] : ns,
       options?.keyPrefix,
     ),
-    i18n: i18nextInstance,
+    i18n: i18nInstance,
   };
 }
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 1f7474c..c3fef07 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -2,62 +2,62 @@
 import type { Config } from "tailwindcss";
 
 const config: Config = {
-    darkMode: ["class"],
-    content: [
+  darkMode: ["class"],
+  content: [
     "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
     "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
     "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
   ],
   theme: {
-  	extend: {
-  		colors: {
-  			background: 'hsl(var(--background))',
-  			foreground: 'hsl(var(--foreground))',
-  			card: {
-  				DEFAULT: 'hsl(var(--card))',
-  				foreground: 'hsl(var(--card-foreground))'
-  			},
-  			popover: {
-  				DEFAULT: 'hsl(var(--popover))',
-  				foreground: 'hsl(var(--popover-foreground))'
-  			},
-  			primary: {
-  				DEFAULT: 'hsl(var(--primary))',
-  				foreground: 'hsl(var(--primary-foreground))'
-  			},
-  			secondary: {
-  				DEFAULT: 'hsl(var(--secondary))',
-  				foreground: 'hsl(var(--secondary-foreground))'
-  			},
-  			muted: {
-  				DEFAULT: 'hsl(var(--muted))',
-  				foreground: 'hsl(var(--muted-foreground))'
-  			},
-  			accent: {
-  				DEFAULT: 'hsl(var(--accent))',
-  				foreground: 'hsl(var(--accent-foreground))'
-  			},
-  			destructive: {
-  				DEFAULT: 'hsl(var(--destructive))',
-  				foreground: 'hsl(var(--destructive-foreground))'
-  			},
-  			border: 'hsl(var(--border))',
-  			input: 'hsl(var(--input))',
-  			ring: 'hsl(var(--ring))',
-  			chart: {
-  				'1': 'hsl(var(--chart-1))',
-  				'2': 'hsl(var(--chart-2))',
-  				'3': 'hsl(var(--chart-3))',
-  				'4': 'hsl(var(--chart-4))',
-  				'5': 'hsl(var(--chart-5))'
-  			}
-  		},
-  		borderRadius: {
-  			lg: 'var(--radius)',
-  			md: 'calc(var(--radius) - 2px)',
-  			sm: 'calc(var(--radius) - 4px)'
-  		}
-  	}
+    extend: {
+      colors: {
+        background: "hsl(var(--background))",
+        foreground: "hsl(var(--foreground))",
+        card: {
+          DEFAULT: "hsl(var(--card))",
+          foreground: "hsl(var(--card-foreground))",
+        },
+        popover: {
+          DEFAULT: "hsl(var(--popover))",
+          foreground: "hsl(var(--popover-foreground))",
+        },
+        primary: {
+          DEFAULT: "hsl(var(--primary))",
+          foreground: "hsl(var(--primary-foreground))",
+        },
+        secondary: {
+          DEFAULT: "hsl(var(--secondary))",
+          foreground: "hsl(var(--secondary-foreground))",
+        },
+        muted: {
+          DEFAULT: "hsl(var(--muted))",
+          foreground: "hsl(var(--muted-foreground))",
+        },
+        accent: {
+          DEFAULT: "hsl(var(--accent))",
+          foreground: "hsl(var(--accent-foreground))",
+        },
+        destructive: {
+          DEFAULT: "hsl(var(--destructive))",
+          foreground: "hsl(var(--destructive-foreground))",
+        },
+        border: "hsl(var(--border))",
+        input: "hsl(var(--input))",
+        ring: "hsl(var(--ring))",
+        chart: {
+          "1": "hsl(var(--chart-1))",
+          "2": "hsl(var(--chart-2))",
+          "3": "hsl(var(--chart-3))",
+          "4": "hsl(var(--chart-4))",
+          "5": "hsl(var(--chart-5))",
+        },
+      },
+      borderRadius: {
+        lg: "var(--radius)",
+        md: "calc(var(--radius) - 2px)",
+        sm: "calc(var(--radius) - 4px)",
+      },
+    },
   },
   plugins: [require("tailwindcss-animate")],
 };
diff --git a/yarn.lock b/yarn.lock
index 827d025..413bcc0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,11 @@
 # yarn lockfile v1
 
 
+"@adobe/css-tools@^4.4.0":
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.1.tgz#2447a230bfe072c1659e6815129c03cf170710e3"
+  integrity sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==
+
 "@alloc/quick-lru@^5.2.0":
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
@@ -15,7 +20,7 @@
     "@jridgewell/gen-mapping" "^0.3.5"
     "@jridgewell/trace-mapping" "^0.3.24"
 
-"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0":
   version "7.26.2"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
   integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
@@ -24,12 +29,12 @@
     js-tokens "^4.0.0"
     picocolors "^1.0.0"
 
-"@babel/compat-data@^7.25.9":
+"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0":
   version "7.26.2"
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e"
   integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==
 
-"@babel/core@^7.25.2":
+"@babel/core@^7.18.9", "@babel/core@^7.24.4", "@babel/core@^7.25.2":
   version "7.26.0"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40"
   integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==
@@ -61,7 +66,22 @@
     "@jridgewell/trace-mapping" "^0.3.25"
     jsesc "^3.0.2"
 
-"@babel/helper-compilation-targets@^7.25.9":
+"@babel/helper-annotate-as-pure@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4"
+  integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==
+  dependencies:
+    "@babel/types" "^7.25.9"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz#f41752fe772a578e67286e6779a68a5a92de1ee9"
+  integrity sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==
+  dependencies:
+    "@babel/traverse" "^7.25.9"
+    "@babel/types" "^7.25.9"
+
+"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9":
   version "7.25.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875"
   integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==
@@ -72,6 +92,47 @@
     lru-cache "^5.1.1"
     semver "^6.3.1"
 
+"@babel/helper-create-class-features-plugin@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83"
+  integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.25.9"
+    "@babel/helper-member-expression-to-functions" "^7.25.9"
+    "@babel/helper-optimise-call-expression" "^7.25.9"
+    "@babel/helper-replace-supers" "^7.25.9"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+    semver "^6.3.1"
+
+"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz#3e8999db94728ad2b2458d7a470e7770b7764e26"
+  integrity sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.25.9"
+    regexpu-core "^6.1.1"
+    semver "^6.3.1"
+
+"@babel/helper-define-polyfill-provider@^0.6.2", "@babel/helper-define-polyfill-provider@^0.6.3":
+  version "0.6.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21"
+  integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==
+  dependencies:
+    "@babel/helper-compilation-targets" "^7.22.6"
+    "@babel/helper-plugin-utils" "^7.22.5"
+    debug "^4.1.1"
+    lodash.debounce "^4.0.8"
+    resolve "^1.14.2"
+
+"@babel/helper-member-expression-to-functions@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3"
+  integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==
+  dependencies:
+    "@babel/traverse" "^7.25.9"
+    "@babel/types" "^7.25.9"
+
 "@babel/helper-module-imports@^7.25.9":
   version "7.25.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
@@ -80,7 +141,7 @@
     "@babel/traverse" "^7.25.9"
     "@babel/types" "^7.25.9"
 
-"@babel/helper-module-transforms@^7.26.0":
+"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0":
   version "7.26.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
   integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
@@ -89,11 +150,52 @@
     "@babel/helper-validator-identifier" "^7.25.9"
     "@babel/traverse" "^7.25.9"
 
-"@babel/helper-plugin-utils@^7.25.9":
+"@babel/helper-optimise-call-expression@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e"
+  integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==
+  dependencies:
+    "@babel/types" "^7.25.9"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0":
   version "7.25.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46"
   integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==
 
+"@babel/helper-remap-async-to-generator@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92"
+  integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.25.9"
+    "@babel/helper-wrap-function" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+
+"@babel/helper-replace-supers@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5"
+  integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.25.9"
+    "@babel/helper-optimise-call-expression" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+
+"@babel/helper-simple-access@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739"
+  integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==
+  dependencies:
+    "@babel/traverse" "^7.25.9"
+    "@babel/types" "^7.25.9"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9"
+  integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==
+  dependencies:
+    "@babel/traverse" "^7.25.9"
+    "@babel/types" "^7.25.9"
+
 "@babel/helper-string-parser@^7.25.9":
   version "7.25.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
@@ -109,6 +211,15 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
   integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==
 
+"@babel/helper-wrap-function@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0"
+  integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==
+  dependencies:
+    "@babel/template" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+    "@babel/types" "^7.25.9"
+
 "@babel/helpers@^7.26.0":
   version "7.26.0"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4"
@@ -124,6 +235,413 @@
   dependencies:
     "@babel/types" "^7.26.0"
 
+"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe"
+  integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30"
+  integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137"
+  integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1"
+  integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+    "@babel/plugin-transform-optional-chaining" "^7.25.9"
+
+"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e"
+  integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2":
+  version "7.21.0-placeholder-for-preset-env.2"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
+  integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
+
+"@babel/plugin-syntax-bigint@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea"
+  integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
+  integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-import-assertions@^7.24.1", "@babel/plugin-syntax-import-assertions@^7.26.0":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f"
+  integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-syntax-import-attributes@^7.26.0":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7"
+  integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-syntax-jsx@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290"
+  integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-syntax-typescript@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399"
+  integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-syntax-unicode-sets-regex@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357"
+  integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.18.6"
+    "@babel/helper-plugin-utils" "^7.18.6"
+
+"@babel/plugin-transform-arrow-functions@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845"
+  integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-async-generator-functions@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz#1b18530b077d18a407c494eb3d1d72da505283a2"
+  integrity sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-remap-async-to-generator" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-transform-async-to-generator@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71"
+  integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==
+  dependencies:
+    "@babel/helper-module-imports" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-remap-async-to-generator" "^7.25.9"
+
+"@babel/plugin-transform-block-scoped-functions@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458"
+  integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-block-scoping@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1"
+  integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-class-properties@^7.24.1", "@babel/plugin-transform-class-properties@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f"
+  integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-class-static-block@^7.26.0":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0"
+  integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-classes@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52"
+  integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.25.9"
+    "@babel/helper-compilation-targets" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-replace-supers" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+    globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b"
+  integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/template" "^7.25.9"
+
+"@babel/plugin-transform-destructuring@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1"
+  integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-dotall-regex@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a"
+  integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-duplicate-keys@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d"
+  integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31"
+  integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-dynamic-import@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8"
+  integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-exponentiation-operator@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz#ece47b70d236c1d99c263a1e22b62dc20a4c8b0f"
+  integrity sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-export-namespace-from@^7.24.1", "@babel/plugin-transform-export-namespace-from@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2"
+  integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-for-of@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755"
+  integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+
+"@babel/plugin-transform-function-name@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97"
+  integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==
+  dependencies:
+    "@babel/helper-compilation-targets" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-transform-json-strings@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660"
+  integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-literals@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de"
+  integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-logical-assignment-operators@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7"
+  integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-member-expression-literals@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de"
+  integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-modules-amd@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5"
+  integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-modules-commonjs@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz#d165c8c569a080baf5467bda88df6425fc060686"
+  integrity sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-simple-access" "^7.25.9"
+
+"@babel/plugin-transform-modules-systemjs@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8"
+  integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-validator-identifier" "^7.25.9"
+    "@babel/traverse" "^7.25.9"
+
+"@babel/plugin-transform-modules-umd@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9"
+  integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a"
+  integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-new-target@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd"
+  integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-nullish-coalescing-operator@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz#bcb1b0d9e948168102d5f7104375ca21c3266949"
+  integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-numeric-separator@^7.24.1", "@babel/plugin-transform-numeric-separator@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1"
+  integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-object-rest-spread@^7.24.1", "@babel/plugin-transform-object-rest-spread@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18"
+  integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==
+  dependencies:
+    "@babel/helper-compilation-targets" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/plugin-transform-parameters" "^7.25.9"
+
+"@babel/plugin-transform-object-super@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03"
+  integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-replace-supers" "^7.25.9"
+
+"@babel/plugin-transform-optional-catch-binding@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3"
+  integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-optional-chaining@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd"
+  integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+
+"@babel/plugin-transform-parameters@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257"
+  integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-private-methods@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57"
+  integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-private-property-in-object@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33"
+  integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.25.9"
+    "@babel/helper-create-class-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-property-literals@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f"
+  integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-react-display-name@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz#4b79746b59efa1f38c8695065a92a9f5afb24f7d"
+  integrity sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-react-jsx-development@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz#8fd220a77dd139c07e25225a903b8be8c829e0d7"
+  integrity sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==
+  dependencies:
+    "@babel/plugin-transform-react-jsx" "^7.25.9"
+
 "@babel/plugin-transform-react-jsx-self@^7.24.7":
   version "7.25.9"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz#c0b6cae9c1b73967f7f9eb2fca9536ba2fad2858"
@@ -138,7 +656,246 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.25.9"
 
-"@babel/runtime@^7.12.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0":
+"@babel/plugin-transform-react-jsx@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz#06367940d8325b36edff5e2b9cbe782947ca4166"
+  integrity sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.25.9"
+    "@babel/helper-module-imports" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/plugin-syntax-jsx" "^7.25.9"
+    "@babel/types" "^7.25.9"
+
+"@babel/plugin-transform-react-pure-annotations@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz#ea1c11b2f9dbb8e2d97025f43a3b5bc47e18ae62"
+  integrity sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-regenerator@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b"
+  integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    regenerator-transform "^0.15.2"
+
+"@babel/plugin-transform-regexp-modifiers@^7.26.0":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850"
+  integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-reserved-words@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce"
+  integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-runtime@^7.24.3":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz#62723ea3f5b31ffbe676da9d6dae17138ae580ea"
+  integrity sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==
+  dependencies:
+    "@babel/helper-module-imports" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    babel-plugin-polyfill-corejs2 "^0.4.10"
+    babel-plugin-polyfill-corejs3 "^0.10.6"
+    babel-plugin-polyfill-regenerator "^0.6.1"
+    semver "^6.3.1"
+
+"@babel/plugin-transform-shorthand-properties@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2"
+  integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-spread@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9"
+  integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+
+"@babel/plugin-transform-sticky-regex@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32"
+  integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-template-literals@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1"
+  integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-typeof-symbol@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz#224ba48a92869ddbf81f9b4a5f1204bbf5a2bc4b"
+  integrity sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-typescript@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz#69267905c2b33c2ac6d8fe765e9dc2ddc9df3849"
+  integrity sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.25.9"
+    "@babel/helper-create-class-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+    "@babel/plugin-syntax-typescript" "^7.25.9"
+
+"@babel/plugin-transform-unicode-escapes@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82"
+  integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-unicode-property-regex@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3"
+  integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-unicode-regex@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1"
+  integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/plugin-transform-unicode-sets-regex@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe"
+  integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+
+"@babel/preset-env@^7.24.4":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.0.tgz#30e5c6bc1bcc54865bff0c5a30f6d4ccdc7fa8b1"
+  integrity sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==
+  dependencies:
+    "@babel/compat-data" "^7.26.0"
+    "@babel/helper-compilation-targets" "^7.25.9"
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-validator-option" "^7.25.9"
+    "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9"
+    "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9"
+    "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9"
+    "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9"
+    "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9"
+    "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2"
+    "@babel/plugin-syntax-import-assertions" "^7.26.0"
+    "@babel/plugin-syntax-import-attributes" "^7.26.0"
+    "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6"
+    "@babel/plugin-transform-arrow-functions" "^7.25.9"
+    "@babel/plugin-transform-async-generator-functions" "^7.25.9"
+    "@babel/plugin-transform-async-to-generator" "^7.25.9"
+    "@babel/plugin-transform-block-scoped-functions" "^7.25.9"
+    "@babel/plugin-transform-block-scoping" "^7.25.9"
+    "@babel/plugin-transform-class-properties" "^7.25.9"
+    "@babel/plugin-transform-class-static-block" "^7.26.0"
+    "@babel/plugin-transform-classes" "^7.25.9"
+    "@babel/plugin-transform-computed-properties" "^7.25.9"
+    "@babel/plugin-transform-destructuring" "^7.25.9"
+    "@babel/plugin-transform-dotall-regex" "^7.25.9"
+    "@babel/plugin-transform-duplicate-keys" "^7.25.9"
+    "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9"
+    "@babel/plugin-transform-dynamic-import" "^7.25.9"
+    "@babel/plugin-transform-exponentiation-operator" "^7.25.9"
+    "@babel/plugin-transform-export-namespace-from" "^7.25.9"
+    "@babel/plugin-transform-for-of" "^7.25.9"
+    "@babel/plugin-transform-function-name" "^7.25.9"
+    "@babel/plugin-transform-json-strings" "^7.25.9"
+    "@babel/plugin-transform-literals" "^7.25.9"
+    "@babel/plugin-transform-logical-assignment-operators" "^7.25.9"
+    "@babel/plugin-transform-member-expression-literals" "^7.25.9"
+    "@babel/plugin-transform-modules-amd" "^7.25.9"
+    "@babel/plugin-transform-modules-commonjs" "^7.25.9"
+    "@babel/plugin-transform-modules-systemjs" "^7.25.9"
+    "@babel/plugin-transform-modules-umd" "^7.25.9"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9"
+    "@babel/plugin-transform-new-target" "^7.25.9"
+    "@babel/plugin-transform-nullish-coalescing-operator" "^7.25.9"
+    "@babel/plugin-transform-numeric-separator" "^7.25.9"
+    "@babel/plugin-transform-object-rest-spread" "^7.25.9"
+    "@babel/plugin-transform-object-super" "^7.25.9"
+    "@babel/plugin-transform-optional-catch-binding" "^7.25.9"
+    "@babel/plugin-transform-optional-chaining" "^7.25.9"
+    "@babel/plugin-transform-parameters" "^7.25.9"
+    "@babel/plugin-transform-private-methods" "^7.25.9"
+    "@babel/plugin-transform-private-property-in-object" "^7.25.9"
+    "@babel/plugin-transform-property-literals" "^7.25.9"
+    "@babel/plugin-transform-regenerator" "^7.25.9"
+    "@babel/plugin-transform-regexp-modifiers" "^7.26.0"
+    "@babel/plugin-transform-reserved-words" "^7.25.9"
+    "@babel/plugin-transform-shorthand-properties" "^7.25.9"
+    "@babel/plugin-transform-spread" "^7.25.9"
+    "@babel/plugin-transform-sticky-regex" "^7.25.9"
+    "@babel/plugin-transform-template-literals" "^7.25.9"
+    "@babel/plugin-transform-typeof-symbol" "^7.25.9"
+    "@babel/plugin-transform-unicode-escapes" "^7.25.9"
+    "@babel/plugin-transform-unicode-property-regex" "^7.25.9"
+    "@babel/plugin-transform-unicode-regex" "^7.25.9"
+    "@babel/plugin-transform-unicode-sets-regex" "^7.25.9"
+    "@babel/preset-modules" "0.1.6-no-external-plugins"
+    babel-plugin-polyfill-corejs2 "^0.4.10"
+    babel-plugin-polyfill-corejs3 "^0.10.6"
+    babel-plugin-polyfill-regenerator "^0.6.1"
+    core-js-compat "^3.38.1"
+    semver "^6.3.1"
+
+"@babel/preset-modules@0.1.6-no-external-plugins":
+  version "0.1.6-no-external-plugins"
+  resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a"
+  integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/types" "^7.4.4"
+    esutils "^2.0.2"
+
+"@babel/preset-react@^7.24.1":
+  version "7.25.9"
+  resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.25.9.tgz#5f473035dc2094bcfdbc7392d0766bd42dce173e"
+  integrity sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-validator-option" "^7.25.9"
+    "@babel/plugin-transform-react-display-name" "^7.25.9"
+    "@babel/plugin-transform-react-jsx" "^7.25.9"
+    "@babel/plugin-transform-react-jsx-development" "^7.25.9"
+    "@babel/plugin-transform-react-pure-annotations" "^7.25.9"
+
+"@babel/preset-typescript@^7.24.1":
+  version "7.26.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d"
+  integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.25.9"
+    "@babel/helper-validator-option" "^7.25.9"
+    "@babel/plugin-syntax-jsx" "^7.25.9"
+    "@babel/plugin-transform-modules-commonjs" "^7.25.9"
+    "@babel/plugin-transform-typescript" "^7.25.9"
+
+"@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.4", "@babel/runtime@^7.25.0", "@babel/runtime@^7.8.4":
   version "7.26.0"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
   integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
@@ -154,7 +911,7 @@
     "@babel/parser" "^7.25.9"
     "@babel/types" "^7.25.9"
 
-"@babel/traverse@^7.25.9":
+"@babel/traverse@^7.18.9", "@babel/traverse@^7.25.9":
   version "7.25.9"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84"
   integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==
@@ -167,7 +924,7 @@
     debug "^4.3.1"
     globals "^11.1.0"
 
-"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0":
+"@babel/types@^7.0.0", "@babel/types@^7.18.9", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.4.4":
   version "7.26.0"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff"
   integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==
@@ -175,6 +932,17 @@
     "@babel/helper-string-parser" "^7.25.9"
     "@babel/helper-validator-identifier" "^7.25.9"
 
+"@chromatic-com/storybook@^3.2.2":
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/@chromatic-com/storybook/-/storybook-3.2.2.tgz#08754443de55618f802f88450c35266fd6d25db5"
+  integrity sha512-xmXt/GW0hAPbzNTrxYuVo43Adrtjue4DeVrsoIIEeJdGaPNNeNf+DHMlJKOBdlHmCnFUoe9R/0mLM9zUp5bKWw==
+  dependencies:
+    chromatic "^11.15.0"
+    filesize "^10.0.12"
+    jsonfile "^6.1.0"
+    react-confetti "^6.1.0"
+    strip-ansi "^7.1.0"
+
 "@emnapi/runtime@^1.2.0":
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60"
@@ -187,116 +955,236 @@
   resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
   integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
 
+"@esbuild/aix-ppc64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz#b57697945b50e99007b4c2521507dc613d4a648c"
+  integrity sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==
+
 "@esbuild/android-arm64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
   integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
 
+"@esbuild/android-arm64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz#1add7e0af67acefd556e407f8497e81fddad79c0"
+  integrity sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==
+
 "@esbuild/android-arm@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
   integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
 
+"@esbuild/android-arm@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.0.tgz#ab7263045fa8e090833a8e3c393b60d59a789810"
+  integrity sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==
+
 "@esbuild/android-x64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
   integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
 
+"@esbuild/android-x64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.0.tgz#e8f8b196cfdfdd5aeaebbdb0110983460440e705"
+  integrity sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==
+
 "@esbuild/darwin-arm64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
   integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
 
+"@esbuild/darwin-arm64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz#2d0d9414f2acbffd2d86e98253914fca603a53dd"
+  integrity sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==
+
 "@esbuild/darwin-x64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
   integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
 
+"@esbuild/darwin-x64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz#33087aab31a1eb64c89daf3d2cf8ce1775656107"
+  integrity sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==
+
 "@esbuild/freebsd-arm64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
   integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
 
+"@esbuild/freebsd-arm64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz#bb76e5ea9e97fa3c753472f19421075d3a33e8a7"
+  integrity sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==
+
 "@esbuild/freebsd-x64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
   integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
 
+"@esbuild/freebsd-x64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz#e0e2ce9249fdf6ee29e5dc3d420c7007fa579b93"
+  integrity sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==
+
 "@esbuild/linux-arm64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
   integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
 
+"@esbuild/linux-arm64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz#d1b2aa58085f73ecf45533c07c82d81235388e75"
+  integrity sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==
+
 "@esbuild/linux-arm@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
   integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
 
+"@esbuild/linux-arm@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz#8e4915df8ea3e12b690a057e77a47b1d5935ef6d"
+  integrity sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==
+
 "@esbuild/linux-ia32@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
   integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
 
+"@esbuild/linux-ia32@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz#8200b1110666c39ab316572324b7af63d82013fb"
+  integrity sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==
+
 "@esbuild/linux-loong64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
   integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
 
+"@esbuild/linux-loong64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz#6ff0c99cf647504df321d0640f0d32e557da745c"
+  integrity sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==
+
 "@esbuild/linux-mips64el@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
   integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
 
+"@esbuild/linux-mips64el@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz#3f720ccd4d59bfeb4c2ce276a46b77ad380fa1f3"
+  integrity sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==
+
 "@esbuild/linux-ppc64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
   integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
 
+"@esbuild/linux-ppc64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz#9d6b188b15c25afd2e213474bf5f31e42e3aa09e"
+  integrity sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==
+
 "@esbuild/linux-riscv64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
   integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
 
+"@esbuild/linux-riscv64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz#f989fdc9752dfda286c9cd87c46248e4dfecbc25"
+  integrity sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==
+
 "@esbuild/linux-s390x@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
   integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
 
+"@esbuild/linux-s390x@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz#29ebf87e4132ea659c1489fce63cd8509d1c7319"
+  integrity sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==
+
 "@esbuild/linux-x64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
   integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
 
+"@esbuild/linux-x64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz#4af48c5c0479569b1f359ffbce22d15f261c0cef"
+  integrity sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==
+
 "@esbuild/netbsd-x64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
   integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
 
+"@esbuild/netbsd-x64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz#1ae73d23cc044a0ebd4f198334416fb26c31366c"
+  integrity sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==
+
+"@esbuild/openbsd-arm64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz#5d904a4f5158c89859fd902c427f96d6a9e632e2"
+  integrity sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==
+
 "@esbuild/openbsd-x64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
   integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
 
+"@esbuild/openbsd-x64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz#4c8aa88c49187c601bae2971e71c6dc5e0ad1cdf"
+  integrity sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==
+
 "@esbuild/sunos-x64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
   integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
 
+"@esbuild/sunos-x64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz#8ddc35a0ea38575fa44eda30a5ee01ae2fa54dd4"
+  integrity sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==
+
 "@esbuild/win32-arm64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
   integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
 
+"@esbuild/win32-arm64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz#6e79c8543f282c4539db684a207ae0e174a9007b"
+  integrity sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==
+
 "@esbuild/win32-ia32@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
   integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
 
+"@esbuild/win32-ia32@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz#057af345da256b7192d18b676a02e95d0fa39103"
+  integrity sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==
+
 "@esbuild/win32-x64@0.21.5":
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
   integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
 
+"@esbuild/win32-x64@0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz#168ab1c7e1c318b922637fad8f339d48b01e1244"
+  integrity sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==
+
 "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
   version "4.4.1"
   resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56"
@@ -509,12 +1397,20 @@
   resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
   integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
 
+"@jridgewell/source-map@^0.3.3":
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a"
+  integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.5"
+    "@jridgewell/trace-mapping" "^0.3.25"
+
 "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
   integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
 
-"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
   version "0.3.25"
   resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
   integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
@@ -537,6 +1433,13 @@
     semver "^7.3.5"
     tar "^6.1.11"
 
+"@mdx-js/react@^3.0.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-3.1.0.tgz#c4522e335b3897b9a845db1dbdd2f966ae8fb0ed"
+  integrity sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==
+  dependencies:
+    "@types/mdx" "^2.0.0"
+
 "@next/env@15.0.2":
   version "15.0.2"
   resolved "https://registry.yarnpkg.com/@next/env/-/env-15.0.2.tgz#4e921af3faf8a16c6be98ec6a81a32a40050a8b7"
@@ -625,6 +1528,19 @@
   resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
   integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
 
+"@pmmmwh/react-refresh-webpack-plugin@^0.5.11":
+  version "0.5.15"
+  resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz#f126be97c30b83ed777e2aeabd518bc592e6e7c4"
+  integrity sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==
+  dependencies:
+    ansi-html "^0.0.9"
+    core-js-pure "^3.23.3"
+    error-stack-parser "^2.0.6"
+    html-entities "^2.1.0"
+    loader-utils "^2.0.4"
+    schema-utils "^4.2.0"
+    source-map "^0.7.3"
+
 "@radix-ui/react-compose-refs@1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74"
@@ -742,6 +1658,347 @@
   resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1"
   integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==
 
+"@storybook/addon-actions@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.4.5.tgz#9224f635c78240f611eb340ba9b4248986b4763c"
+  integrity sha512-rbB19uiGJ61XHbKIbS1a9bUS6re5L8rT5NMNeEJhCxXRpFUPrlTXMSoD/Pgcn3ENeEMVZsm8/eCzxAVgAP3Mgg==
+  dependencies:
+    "@storybook/global" "^5.0.0"
+    "@types/uuid" "^9.0.1"
+    dequal "^2.0.2"
+    polished "^4.2.2"
+    uuid "^9.0.0"
+
+"@storybook/addon-backgrounds@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.5.tgz#3d93d24d0b8e77d6d5bad78bae4c8621ee27c5d1"
+  integrity sha512-FeMt4qHCMYDQiLGGDKiRuSPXFup2WXOaZSdL137v1W36wEL/vGkK1A5iQt1qJ8MZzL5WZQuedox8rSybFy7eow==
+  dependencies:
+    "@storybook/global" "^5.0.0"
+    memoizerific "^1.11.3"
+    ts-dedent "^2.0.0"
+
+"@storybook/addon-controls@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-8.4.5.tgz#1f6ce4044ab093aabb79fcd6537a996f2d12597f"
+  integrity sha512-RVTtDDuESLYc1+SJQv2kI7wzBddzAS9uoEe8P75quN6S4pC0GxAB6xirWZ2+WOcba4eHosY+PxMwuBXQfH78Ew==
+  dependencies:
+    "@storybook/global" "^5.0.0"
+    dequal "^2.0.2"
+    ts-dedent "^2.0.0"
+
+"@storybook/addon-docs@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-8.4.5.tgz#fa38b6c6c0ef1c8fc588b6913ddb55b67be55666"
+  integrity sha512-zPELIl7wXormOylVaaSpkUIuuCCxrO+OFPMKZnlENt6zSReyy0dJu4V0tzfV8FCw+V4D6Y4wrLRk/TIG951Ojw==
+  dependencies:
+    "@mdx-js/react" "^3.0.0"
+    "@storybook/blocks" "8.4.5"
+    "@storybook/csf-plugin" "8.4.5"
+    "@storybook/react-dom-shim" "8.4.5"
+    react "^16.8.0 || ^17.0.0 || ^18.0.0"
+    react-dom "^16.8.0 || ^17.0.0 || ^18.0.0"
+    ts-dedent "^2.0.0"
+
+"@storybook/addon-essentials@^8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-8.4.5.tgz#7ff4f71b4435f734936272a691a830a34eaaef49"
+  integrity sha512-AxetQo/zSPIu3RZqWG2opwAz22Bb+jpf1nWbHp0kEpCrBemcWd8X2gonVmXNOC1PDKNl3jcWyc3lmg/+3mxjYg==
+  dependencies:
+    "@storybook/addon-actions" "8.4.5"
+    "@storybook/addon-backgrounds" "8.4.5"
+    "@storybook/addon-controls" "8.4.5"
+    "@storybook/addon-docs" "8.4.5"
+    "@storybook/addon-highlight" "8.4.5"
+    "@storybook/addon-measure" "8.4.5"
+    "@storybook/addon-outline" "8.4.5"
+    "@storybook/addon-toolbars" "8.4.5"
+    "@storybook/addon-viewport" "8.4.5"
+    ts-dedent "^2.0.0"
+
+"@storybook/addon-highlight@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-highlight/-/addon-highlight-8.4.5.tgz#44c194f88395a6e221744867a11f39ea8910d290"
+  integrity sha512-sMA7v+4unaKY+5RDhow6lLncJqNX9ZLUnBIt3vzY1ntUsOYVwykAY1Hq4Ysj0luCBXjJJdJ6223ylrycnb7Ilw==
+  dependencies:
+    "@storybook/global" "^5.0.0"
+
+"@storybook/addon-interactions@^8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-8.4.5.tgz#0053207da50a4cefc9dce46b7980cd54a828c2ff"
+  integrity sha512-s6R8XVD8LTp+LQTDbhtDjDLE6S44I7FtMLxPdMNwN9VEJjBk01NONLDuGDpNq5o/0bnybA3rMHk9+3afsgzidQ==
+  dependencies:
+    "@storybook/global" "^5.0.0"
+    "@storybook/instrumenter" "8.4.5"
+    "@storybook/test" "8.4.5"
+    polished "^4.2.2"
+    ts-dedent "^2.2.0"
+
+"@storybook/addon-measure@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-8.4.5.tgz#9b22ea0f28740f0c9c1edee2642de2b75415abdd"
+  integrity sha512-+sNjew991YaoXQyWWloFybjEGrDO40Jk6w8BgZs2X7oc3D5t/6oFzvyC862U++LGqKFA3quXDeBjEb92CI9cRA==
+  dependencies:
+    "@storybook/global" "^5.0.0"
+    tiny-invariant "^1.3.1"
+
+"@storybook/addon-onboarding@^8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-onboarding/-/addon-onboarding-8.4.5.tgz#731ac4a27711dff565dec9b4090fb49ab752d7b9"
+  integrity sha512-+FW50yVw2NMxYvk3uMpIberfkG4Sn0qRpiMse7MGHgTimtaJ0Mo1AUIrSfyIJCVTuxiWZud1a5DAnH0ybbWjjA==
+  dependencies:
+    react-confetti "^6.1.0"
+
+"@storybook/addon-outline@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-8.4.5.tgz#32d0a798b7c5abacbe2576125f589c8d4bcd5bcc"
+  integrity sha512-XlpN98AUDnWQWNFSFVm+HkRUzm3xIUMjBGTkv6HsL6zt6XoJ+LsQMca+PPtYqlBJA+5CU41xMDaG8HC/p+sd3A==
+  dependencies:
+    "@storybook/global" "^5.0.0"
+    ts-dedent "^2.0.0"
+
+"@storybook/addon-toolbars@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-8.4.5.tgz#ebe20f0577a3e399858ba591066dbc8ac9636a5c"
+  integrity sha512-hOq5560ONOU/qrslrwosWzxnC4nrF8HZWD43ciKwtethm8HuptU2M+Jrui1CRsMScEZLopWWVE9o0vJMdKpIFQ==
+
+"@storybook/addon-viewport@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-8.4.5.tgz#21a8b6d49f31cdca72018e9cd3adffbc435fa61a"
+  integrity sha512-l7Y41gIbJAsIN/QCg1QJ9sr61FLz1C/imUotcDej41tOHxUTSQOlXpNtVnfhUM1vGQc0yNpP3pVxj8BpXi0cAw==
+  dependencies:
+    memoizerific "^1.11.3"
+
+"@storybook/blocks@8.4.5", "@storybook/blocks@^8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/blocks/-/blocks-8.4.5.tgz#9494d9ccddc04bacd6a7a6c67cf870b72bf4ce9d"
+  integrity sha512-Z+LHauSqm3A4HBR9pUEf9KQhD3/3xYMt0FXgA+GHCAyDa6lFeD1C6r9Y2nlT+9dt8gv9B9oygTZvV6GqFVyRSQ==
+  dependencies:
+    "@storybook/csf" "^0.1.11"
+    "@storybook/icons" "^1.2.12"
+    ts-dedent "^2.0.0"
+
+"@storybook/builder-webpack5@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-8.4.5.tgz#f2c15eb2219dc21bc6b0f4a5f83afd8c80561bff"
+  integrity sha512-5TSpirK2LIL4Wultpowlkrv3iAje57HTw92Hy6c4Zn64tAs30123mkdE6MoJcXMBfD4JwX9I2K2Q+ofZXblJPg==
+  dependencies:
+    "@storybook/core-webpack" "8.4.5"
+    "@types/node" "^22.0.0"
+    "@types/semver" "^7.3.4"
+    browser-assert "^1.2.1"
+    case-sensitive-paths-webpack-plugin "^2.4.0"
+    cjs-module-lexer "^1.2.3"
+    constants-browserify "^1.0.0"
+    css-loader "^6.7.1"
+    es-module-lexer "^1.5.0"
+    fork-ts-checker-webpack-plugin "^8.0.0"
+    html-webpack-plugin "^5.5.0"
+    magic-string "^0.30.5"
+    path-browserify "^1.0.1"
+    process "^0.11.10"
+    semver "^7.3.7"
+    style-loader "^3.3.1"
+    terser-webpack-plugin "^5.3.1"
+    ts-dedent "^2.0.0"
+    url "^0.11.0"
+    util "^0.12.4"
+    util-deprecate "^1.0.2"
+    webpack "5"
+    webpack-dev-middleware "^6.1.2"
+    webpack-hot-middleware "^2.25.1"
+    webpack-virtual-modules "^0.6.0"
+
+"@storybook/components@8.4.5", "@storybook/components@^8.0.0":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/components/-/components-8.4.5.tgz#039e4cb5090c56c47fc10f0c5ecda77f4b015c7f"
+  integrity sha512-2PdnKfqNNv3sO7qILgWXiNvmLOi503oN9OMemNCQjTIvdvySc5JpS9/eClwcl/JfmE4qHdSHZr8dLLkBM9S7+Q==
+
+"@storybook/core-events@^8.0.0":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-8.4.5.tgz#b2847bdbeb964019a9d134a311511b7d227e1682"
+  integrity sha512-+DeYpAuav9E/Q5x5BVrMpf+XBFbVQJuJbVXNiglZcJNH13D/G2WDToOYAbGviZ6CBvbUdjlqC7y9+/emwP78bA==
+
+"@storybook/core-webpack@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/core-webpack/-/core-webpack-8.4.5.tgz#b17422cff80c638e3478406193acedb9be2d50cb"
+  integrity sha512-IpK/3fM+l2WjRNplTtP+MtnRf/394GcBwyemZknUCzFFDJWNYAN1+meEZmOaZKzJ3tQyRYiErrJLHzd1+UH6Dw==
+  dependencies:
+    "@types/node" "^22.0.0"
+    ts-dedent "^2.0.0"
+
+"@storybook/core@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/core/-/core-8.4.5.tgz#4133927a43834664777f0918caf32542630cbdd5"
+  integrity sha512-aB1sQNX5nRoUAqg5u1py0MuR/VPd6c6PhECa4rW6pmr7kZcfyP4PP6UFpXuN71ypTQlkRE3Vc5PQZ3gLhE9o3g==
+  dependencies:
+    "@storybook/csf" "^0.1.11"
+    better-opn "^3.0.2"
+    browser-assert "^1.2.1"
+    esbuild "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0"
+    esbuild-register "^3.5.0"
+    jsdoc-type-pratt-parser "^4.0.0"
+    process "^0.11.10"
+    recast "^0.23.5"
+    semver "^7.6.2"
+    util "^0.12.5"
+    ws "^8.2.3"
+
+"@storybook/csf-plugin@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-8.4.5.tgz#7f9ae0176e36cb74b1f638aac04dfec4aa04222f"
+  integrity sha512-qd2rQTglOTS+phQmTbNTXNjNyxdGvolaqHqDNMw3Vf6h9o3U+mLkwnDWNVnQ9oqvOoUEAqpBthgwzU9FhkIk+A==
+  dependencies:
+    unplugin "^1.3.1"
+
+"@storybook/csf@^0.1.11":
+  version "0.1.11"
+  resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.1.11.tgz#ad685a4fe564a47a6b73571c2e7c07b526f4f71b"
+  integrity sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==
+  dependencies:
+    type-fest "^2.19.0"
+
+"@storybook/global@^5.0.0":
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/@storybook/global/-/global-5.0.0.tgz#b793d34b94f572c1d7d9e0f44fac4e0dbc9572ed"
+  integrity sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==
+
+"@storybook/icons@^1.2.12", "@storybook/icons@^1.2.5":
+  version "1.2.12"
+  resolved "https://registry.yarnpkg.com/@storybook/icons/-/icons-1.2.12.tgz#3e4c939113b67df7ab17b78f805dbb57f4acf0db"
+  integrity sha512-UxgyK5W3/UV4VrI3dl6ajGfHM4aOqMAkFLWe2KibeQudLf6NJpDrDMSHwZj+3iKC4jFU7dkKbbtH2h/al4sW3Q==
+
+"@storybook/instrumenter@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-8.4.5.tgz#14983198b27dfbeedfef28992eef0d304b9f562b"
+  integrity sha512-8qM35FkueuRpJr0zA6ENvhQICbo+iKL1ln450DwV1kKJtc41KdbA3CuCvtZ/FnoPsFnwdtPjhhICFtRt8LRTSg==
+  dependencies:
+    "@storybook/global" "^5.0.0"
+    "@vitest/utils" "^2.1.1"
+
+"@storybook/manager-api@8.4.5", "@storybook/manager-api@^8.0.0":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-8.4.5.tgz#ce586cc5961297272f7228446161015464c65067"
+  integrity sha512-t39JaMy3UX4StbUH/tIDcaflBDxTcyIq853wQtBMhVL3e1+Dw3MIiiG/5bw79HU4R7kSmPVLXIIbV3FmXkq7KQ==
+
+"@storybook/nextjs@^8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/nextjs/-/nextjs-8.4.5.tgz#f930db51ae06fb10ecd8b6d6af8177de5565b2e3"
+  integrity sha512-KhP9XVI20iwAvMFHqvlV0x5UqzvMbD42QjSW5/2KYy52CStnczfa/3Xyb2VBgAMQx3Ony0qnqlTVHi6qhJQtOA==
+  dependencies:
+    "@babel/core" "^7.24.4"
+    "@babel/plugin-syntax-bigint" "^7.8.3"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+    "@babel/plugin-syntax-import-assertions" "^7.24.1"
+    "@babel/plugin-transform-class-properties" "^7.24.1"
+    "@babel/plugin-transform-export-namespace-from" "^7.24.1"
+    "@babel/plugin-transform-numeric-separator" "^7.24.1"
+    "@babel/plugin-transform-object-rest-spread" "^7.24.1"
+    "@babel/plugin-transform-runtime" "^7.24.3"
+    "@babel/preset-env" "^7.24.4"
+    "@babel/preset-react" "^7.24.1"
+    "@babel/preset-typescript" "^7.24.1"
+    "@babel/runtime" "^7.24.4"
+    "@pmmmwh/react-refresh-webpack-plugin" "^0.5.11"
+    "@storybook/builder-webpack5" "8.4.5"
+    "@storybook/preset-react-webpack" "8.4.5"
+    "@storybook/react" "8.4.5"
+    "@storybook/test" "8.4.5"
+    "@types/node" "^22.0.0"
+    "@types/semver" "^7.3.4"
+    babel-loader "^9.1.3"
+    css-loader "^6.7.3"
+    find-up "^5.0.0"
+    image-size "^1.0.0"
+    loader-utils "^3.2.1"
+    node-polyfill-webpack-plugin "^2.0.1"
+    pnp-webpack-plugin "^1.7.0"
+    postcss "^8.4.38"
+    postcss-loader "^8.1.1"
+    react-refresh "^0.14.0"
+    resolve-url-loader "^5.0.0"
+    sass-loader "^13.2.0"
+    semver "^7.3.5"
+    style-loader "^3.3.1"
+    styled-jsx "^5.1.6"
+    ts-dedent "^2.0.0"
+    tsconfig-paths "^4.0.0"
+    tsconfig-paths-webpack-plugin "^4.0.1"
+  optionalDependencies:
+    sharp "^0.33.3"
+
+"@storybook/preset-react-webpack@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.5.tgz#da27afe7eaacaa17816608b1ccf225eb986965d8"
+  integrity sha512-BKPAN7G0yFXfojQdF8tvgwVJ0ldcl6+p1JtAPAieH69BMGni3TEPnvPhkefRWcM8oM8pl+Hch/J2PLHiZ6QKNQ==
+  dependencies:
+    "@storybook/core-webpack" "8.4.5"
+    "@storybook/react" "8.4.5"
+    "@storybook/react-docgen-typescript-plugin" "1.0.6--canary.9.0c3f3b7.0"
+    "@types/node" "^22.0.0"
+    "@types/semver" "^7.3.4"
+    find-up "^5.0.0"
+    magic-string "^0.30.5"
+    react-docgen "^7.0.0"
+    resolve "^1.22.8"
+    semver "^7.3.7"
+    tsconfig-paths "^4.2.0"
+    webpack "5"
+
+"@storybook/preview-api@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-8.4.5.tgz#f8c046575c68062eda1de9494e7118571d20c72a"
+  integrity sha512-MKIZ2jQO/3cUdsT57eq8jRgB6inALo9BxrQ88f7mqzltOkMvADvTAY6y8JZqTUoDzWTH/ny/8SGGdtpqlxRuiQ==
+
+"@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0":
+  version "1.0.6--canary.9.0c3f3b7.0"
+  resolved "https://registry.yarnpkg.com/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.0c3f3b7.0.tgz#7f10f3c641f32e4513a8b6ffb5036933e7059534"
+  integrity sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==
+  dependencies:
+    debug "^4.1.1"
+    endent "^2.0.1"
+    find-cache-dir "^3.3.1"
+    flat-cache "^3.0.4"
+    micromatch "^4.0.2"
+    react-docgen-typescript "^2.2.2"
+    tslib "^2.0.0"
+
+"@storybook/react-dom-shim@8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-8.4.5.tgz#cc874d008a1def4410c32a95bcf9aac8defdb6f5"
+  integrity sha512-YTWTfPagptEYXJsnxAl3zP97Ev0zebtaEV0WgjGaEeumr+zsfgKKwzzHxgrtumBmDzwkuKlzFwlQB5A8keOIGA==
+
+"@storybook/react@8.4.5", "@storybook/react@^8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/react/-/react-8.4.5.tgz#9395ce8ffd4e27765459703c0634b306af882137"
+  integrity sha512-2+p4aGEdGOnu2XNhnMi1B8GPeszm34P905HgqGD1cuz9gMt7x/bgZQaVxs6kpHZ3Hb6V9qp62La2dbAYatHdSw==
+  dependencies:
+    "@storybook/components" "8.4.5"
+    "@storybook/global" "^5.0.0"
+    "@storybook/manager-api" "8.4.5"
+    "@storybook/preview-api" "8.4.5"
+    "@storybook/react-dom-shim" "8.4.5"
+    "@storybook/theming" "8.4.5"
+
+"@storybook/test@8.4.5", "@storybook/test@^8.4.5":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/test/-/test-8.4.5.tgz#a22e8a4671c379f8ffc83a3997feb524bf5d9421"
+  integrity sha512-mHsRc6m60nfcEBsjvUkKz+Jnz0or4WH5jmJ1VL2pGKO4VzESCPqAwDnwDqP2YyeSQ0b/MAKUT5kdoLE2RE2eVw==
+  dependencies:
+    "@storybook/csf" "^0.1.11"
+    "@storybook/global" "^5.0.0"
+    "@storybook/instrumenter" "8.4.5"
+    "@testing-library/dom" "10.4.0"
+    "@testing-library/jest-dom" "6.5.0"
+    "@testing-library/user-event" "14.5.2"
+    "@vitest/expect" "2.0.5"
+    "@vitest/spy" "2.0.5"
+
+"@storybook/theming@8.4.5", "@storybook/theming@^8.0.0":
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-8.4.5.tgz#d7c77215f09906c9bb124d530d4307fd1841fbb8"
+  integrity sha512-45e/jeG4iuqdZcHg3PbB6dwXQTwlnnEB7r/QcVExyC7ibrkTnjUfvxzyUw4mmU3CXETFGD5EcUobFkgK+/aPxQ==
+
 "@swc/counter@0.1.3":
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9"
@@ -754,7 +2011,7 @@
   dependencies:
     tslib "^2.4.0"
 
-"@testing-library/dom@^10.4.0":
+"@testing-library/dom@10.4.0", "@testing-library/dom@^10.4.0":
   version "10.4.0"
   resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8"
   integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==
@@ -768,6 +2025,19 @@
     lz-string "^1.5.0"
     pretty-format "^27.0.2"
 
+"@testing-library/jest-dom@6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz#50484da3f80fb222a853479f618a9ce5c47bfe54"
+  integrity sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==
+  dependencies:
+    "@adobe/css-tools" "^4.4.0"
+    aria-query "^5.0.0"
+    chalk "^3.0.0"
+    css.escape "^1.5.1"
+    dom-accessibility-api "^0.6.3"
+    lodash "^4.17.21"
+    redent "^3.0.0"
+
 "@testing-library/react@^16.0.1":
   version "16.0.1"
   resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.0.1.tgz#29c0ee878d672703f5e7579f239005e4e0faa875"
@@ -775,12 +2045,17 @@
   dependencies:
     "@babel/runtime" "^7.12.5"
 
+"@testing-library/user-event@14.5.2":
+  version "14.5.2"
+  resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.2.tgz#db7257d727c891905947bd1c1a99da20e03c2ebd"
+  integrity sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==
+
 "@types/aria-query@^5.0.1":
   version "5.0.4"
   resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708"
   integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
 
-"@types/babel__core@^7.20.5":
+"@types/babel__core@^7.18.0", "@types/babel__core@^7.20.5":
   version "7.20.5"
   resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
   integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
@@ -806,7 +2081,7 @@
     "@babel/parser" "^7.1.0"
     "@babel/types" "^7.0.0"
 
-"@types/babel__traverse@*":
+"@types/babel__traverse@*", "@types/babel__traverse@^7.18.0":
   version "7.20.6"
   resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7"
   integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==
@@ -818,7 +2093,28 @@
   resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5"
   integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==
 
-"@types/estree@1.0.6", "@types/estree@^1.0.0":
+"@types/doctrine@^0.0.9":
+  version "0.0.9"
+  resolved "https://registry.yarnpkg.com/@types/doctrine/-/doctrine-0.0.9.tgz#d86a5f452a15e3e3113b99e39616a9baa0f9863f"
+  integrity sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==
+
+"@types/eslint-scope@^3.7.7":
+  version "3.7.7"
+  resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5"
+  integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==
+  dependencies:
+    "@types/eslint" "*"
+    "@types/estree" "*"
+
+"@types/eslint@*":
+  version "9.6.1"
+  resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584"
+  integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==
+  dependencies:
+    "@types/estree" "*"
+    "@types/json-schema" "*"
+
+"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6":
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
   integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
@@ -831,11 +2127,33 @@
     "@types/react" "*"
     hoist-non-react-statics "^3.3.0"
 
+"@types/html-minifier-terser@^6.0.0":
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
+  integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==
+
+"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+  version "7.0.15"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+  integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
   integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
 
+"@types/mdx@^2.0.0":
+  version "2.0.13"
+  resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.13.tgz#68f6877043d377092890ff5b298152b0a21671bd"
+  integrity sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==
+
+"@types/node@*", "@types/node@^22.0.0":
+  version "22.9.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.1.tgz#bdf91c36e0e7ecfb7257b2d75bf1b206b308ca71"
+  integrity sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==
+  dependencies:
+    undici-types "~6.19.8"
+
 "@types/node@^20":
   version "20.17.2"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.2.tgz#3ca40ef7d776c85a1db3df23cbb5bfb3c384a92e"
@@ -843,6 +2161,11 @@
   dependencies:
     undici-types "~6.19.2"
 
+"@types/parse-json@^4.0.0":
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239"
+  integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==
+
 "@types/prop-types@*":
   version "15.7.13"
   resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451"
@@ -863,6 +2186,21 @@
     "@types/prop-types" "*"
     csstype "^3.0.2"
 
+"@types/resolve@^1.20.2":
+  version "1.20.6"
+  resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.6.tgz#e6e60dad29c2c8c206c026e6dd8d6d1bdda850b8"
+  integrity sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==
+
+"@types/semver@^7.3.4":
+  version "7.5.8"
+  resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
+  integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
+
+"@types/uuid@^9.0.1":
+  version "9.0.8"
+  resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba"
+  integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==
+
 "@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
   version "8.12.2"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz#c2ef660bb83fd1432368319312a2581fc92ccac1"
@@ -897,6 +2235,14 @@
     "@typescript-eslint/types" "8.12.2"
     "@typescript-eslint/visitor-keys" "8.12.2"
 
+"@typescript-eslint/scope-manager@8.15.0":
+  version "8.15.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz#28a1a0f13038f382424f45a988961acaca38f7c6"
+  integrity sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==
+  dependencies:
+    "@typescript-eslint/types" "8.15.0"
+    "@typescript-eslint/visitor-keys" "8.15.0"
+
 "@typescript-eslint/type-utils@8.12.2":
   version "8.12.2"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz#132b0c52d45f6814e6f2e32416c7951ed480b016"
@@ -912,6 +2258,11 @@
   resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.12.2.tgz#8d70098c0e90442495b53d0296acdca6d0f3f73c"
   integrity sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==
 
+"@typescript-eslint/types@8.15.0":
+  version "8.15.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.15.0.tgz#4958edf3d83e97f77005f794452e595aaf6430fc"
+  integrity sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==
+
 "@typescript-eslint/typescript-estree@8.12.2":
   version "8.12.2"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz#206df9b1cbff212aaa9401985ef99f04daa84da5"
@@ -926,6 +2277,20 @@
     semver "^7.6.0"
     ts-api-utils "^1.3.0"
 
+"@typescript-eslint/typescript-estree@8.15.0":
+  version "8.15.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz#915c94e387892b114a2a2cc0df2d7f19412c8ba7"
+  integrity sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==
+  dependencies:
+    "@typescript-eslint/types" "8.15.0"
+    "@typescript-eslint/visitor-keys" "8.15.0"
+    debug "^4.3.4"
+    fast-glob "^3.3.2"
+    is-glob "^4.0.3"
+    minimatch "^9.0.4"
+    semver "^7.6.0"
+    ts-api-utils "^1.3.0"
+
 "@typescript-eslint/utils@8.12.2":
   version "8.12.2"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.12.2.tgz#726cc9f49f5866605bd15bbc1768ffc15637930e"
@@ -936,6 +2301,16 @@
     "@typescript-eslint/types" "8.12.2"
     "@typescript-eslint/typescript-estree" "8.12.2"
 
+"@typescript-eslint/utils@^8.8.1":
+  version "8.15.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.15.0.tgz#ac04679ad19252776b38b81954b8e5a65567cef6"
+  integrity sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.4.0"
+    "@typescript-eslint/scope-manager" "8.15.0"
+    "@typescript-eslint/types" "8.15.0"
+    "@typescript-eslint/typescript-estree" "8.15.0"
+
 "@typescript-eslint/visitor-keys@8.12.2":
   version "8.12.2"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz#94d7410f78eb6d134b9fcabaf1eeedb910ba8c38"
@@ -944,6 +2319,14 @@
     "@typescript-eslint/types" "8.12.2"
     eslint-visitor-keys "^3.4.3"
 
+"@typescript-eslint/visitor-keys@8.15.0":
+  version "8.15.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz#9ea5a85eb25401d2aa74ec8a478af4e97899ea12"
+  integrity sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==
+  dependencies:
+    "@typescript-eslint/types" "8.15.0"
+    eslint-visitor-keys "^4.2.0"
+
 "@ungap/structured-clone@^1.2.0":
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
@@ -960,6 +2343,16 @@
     "@types/babel__core" "^7.20.5"
     react-refresh "^0.14.2"
 
+"@vitest/expect@2.0.5":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.0.5.tgz#f3745a6a2c18acbea4d39f5935e913f40d26fa86"
+  integrity sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==
+  dependencies:
+    "@vitest/spy" "2.0.5"
+    "@vitest/utils" "2.0.5"
+    chai "^5.1.1"
+    tinyrainbow "^1.2.0"
+
 "@vitest/expect@2.1.4":
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.4.tgz#48f4f53a01092a3bdc118cff245f79ef388bdd8e"
@@ -979,6 +2372,13 @@
     estree-walker "^3.0.3"
     magic-string "^0.30.12"
 
+"@vitest/pretty-format@2.0.5":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.0.5.tgz#91d2e6d3a7235c742e1a6cc50e7786e2f2979b1e"
+  integrity sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==
+  dependencies:
+    tinyrainbow "^1.2.0"
+
 "@vitest/pretty-format@2.1.4", "@vitest/pretty-format@^2.1.4":
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.4.tgz#fc31993bdc1ef5a6c1a4aa6844e7ba55658a4f9f"
@@ -986,6 +2386,13 @@
   dependencies:
     tinyrainbow "^1.2.0"
 
+"@vitest/pretty-format@2.1.5":
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.5.tgz#bc79b8826d4a63dc04f2a75d2944694039fa50aa"
+  integrity sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==
+  dependencies:
+    tinyrainbow "^1.2.0"
+
 "@vitest/runner@2.1.4":
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.4.tgz#f9346500bdd0be1c926daaac5d683bae87ceda2c"
@@ -1003,6 +2410,13 @@
     magic-string "^0.30.12"
     pathe "^1.1.2"
 
+"@vitest/spy@2.0.5":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.0.5.tgz#590fc07df84a78b8e9dd976ec2090920084a2b9f"
+  integrity sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==
+  dependencies:
+    tinyspy "^3.0.0"
+
 "@vitest/spy@2.1.4":
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.4.tgz#4e90f9783437c5841a27c80f8fd84d7289a6100a"
@@ -1010,6 +2424,16 @@
   dependencies:
     tinyspy "^3.0.2"
 
+"@vitest/utils@2.0.5":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.0.5.tgz#6f8307a4b6bc6ceb9270007f73c67c915944e926"
+  integrity sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==
+  dependencies:
+    "@vitest/pretty-format" "2.0.5"
+    estree-walker "^3.0.3"
+    loupe "^3.1.1"
+    tinyrainbow "^1.2.0"
+
 "@vitest/utils@2.1.4":
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.4.tgz#6d67ac966647a21ce8bc497472ce230de3b64537"
@@ -1019,11 +2443,158 @@
     loupe "^3.1.2"
     tinyrainbow "^1.2.0"
 
+"@vitest/utils@^2.1.1":
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.5.tgz#0e19ce677c870830a1573d33ee86b0d6109e9546"
+  integrity sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==
+  dependencies:
+    "@vitest/pretty-format" "2.1.5"
+    loupe "^3.1.2"
+    tinyrainbow "^1.2.0"
+
+"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6"
+  integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==
+  dependencies:
+    "@webassemblyjs/helper-numbers" "1.13.2"
+    "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+
+"@webassemblyjs/floating-point-hex-parser@1.13.2":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb"
+  integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==
+
+"@webassemblyjs/helper-api-error@1.13.2":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7"
+  integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==
+
+"@webassemblyjs/helper-buffer@1.14.1":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b"
+  integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==
+
+"@webassemblyjs/helper-numbers@1.13.2":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d"
+  integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==
+  dependencies:
+    "@webassemblyjs/floating-point-hex-parser" "1.13.2"
+    "@webassemblyjs/helper-api-error" "1.13.2"
+    "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/helper-wasm-bytecode@1.13.2":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b"
+  integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==
+
+"@webassemblyjs/helper-wasm-section@1.14.1":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348"
+  integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==
+  dependencies:
+    "@webassemblyjs/ast" "1.14.1"
+    "@webassemblyjs/helper-buffer" "1.14.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+    "@webassemblyjs/wasm-gen" "1.14.1"
+
+"@webassemblyjs/ieee754@1.13.2":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba"
+  integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==
+  dependencies:
+    "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.13.2":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0"
+  integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==
+  dependencies:
+    "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.13.2":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1"
+  integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==
+
+"@webassemblyjs/wasm-edit@^1.12.1":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597"
+  integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==
+  dependencies:
+    "@webassemblyjs/ast" "1.14.1"
+    "@webassemblyjs/helper-buffer" "1.14.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+    "@webassemblyjs/helper-wasm-section" "1.14.1"
+    "@webassemblyjs/wasm-gen" "1.14.1"
+    "@webassemblyjs/wasm-opt" "1.14.1"
+    "@webassemblyjs/wasm-parser" "1.14.1"
+    "@webassemblyjs/wast-printer" "1.14.1"
+
+"@webassemblyjs/wasm-gen@1.14.1":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570"
+  integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==
+  dependencies:
+    "@webassemblyjs/ast" "1.14.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+    "@webassemblyjs/ieee754" "1.13.2"
+    "@webassemblyjs/leb128" "1.13.2"
+    "@webassemblyjs/utf8" "1.13.2"
+
+"@webassemblyjs/wasm-opt@1.14.1":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b"
+  integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==
+  dependencies:
+    "@webassemblyjs/ast" "1.14.1"
+    "@webassemblyjs/helper-buffer" "1.14.1"
+    "@webassemblyjs/wasm-gen" "1.14.1"
+    "@webassemblyjs/wasm-parser" "1.14.1"
+
+"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.12.1":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb"
+  integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==
+  dependencies:
+    "@webassemblyjs/ast" "1.14.1"
+    "@webassemblyjs/helper-api-error" "1.13.2"
+    "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+    "@webassemblyjs/ieee754" "1.13.2"
+    "@webassemblyjs/leb128" "1.13.2"
+    "@webassemblyjs/utf8" "1.13.2"
+
+"@webassemblyjs/wast-printer@1.14.1":
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07"
+  integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==
+  dependencies:
+    "@webassemblyjs/ast" "1.14.1"
+    "@xtuc/long" "4.2.2"
+
+"@xtuc/ieee754@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+  integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
+  integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
 abbrev@1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
   integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
 
+abort-controller@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
+  integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
+  dependencies:
+    event-target-shim "^5.0.0"
+
 accept-language@^3.0.20:
   version "3.0.20"
   resolved "https://registry.yarnpkg.com/accept-language/-/accept-language-3.0.20.tgz#e825601d3b59f5ac7487698569b6640e80240cda"
@@ -1036,11 +2607,19 @@ acorn-jsx@^5.3.2:
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
   integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
 
-acorn@^8.9.0:
+acorn@^8.14.0, acorn@^8.8.2, acorn@^8.9.0:
   version "8.14.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
   integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
 
+adjust-sourcemap-loader@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz#fc4a0fd080f7d10471f30a7320f25560ade28c99"
+  integrity sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==
+  dependencies:
+    loader-utils "^2.0.0"
+    regex-parser "^2.2.11"
+
 agent-base@6:
   version "6.0.2"
   resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -1055,7 +2634,26 @@ agent-base@^7.0.2, agent-base@^7.1.0:
   dependencies:
     debug "^4.3.4"
 
-ajv@^6.12.4:
+ajv-formats@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
+  integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
+  dependencies:
+    ajv "^8.0.0"
+
+ajv-keywords@^3.5.2:
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
+  integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
+
+ajv-keywords@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16"
+  integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==
+  dependencies:
+    fast-deep-equal "^3.1.3"
+
+ajv@^6.12.4, ajv@^6.12.5:
   version "6.12.6"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
   integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -1065,6 +2663,26 @@ ajv@^6.12.4:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
+ajv@^8.0.0, ajv@^8.9.0:
+  version "8.17.1"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
+  integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
+  dependencies:
+    fast-deep-equal "^3.1.3"
+    fast-uri "^3.0.1"
+    json-schema-traverse "^1.0.0"
+    require-from-string "^2.0.2"
+
+ansi-html-community@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
+  integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
+
+ansi-html@^0.0.9:
+  version "0.0.9"
+  resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.9.tgz#6512d02342ae2cc68131952644a129cb734cd3f0"
+  integrity sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==
+
 ansi-regex@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
@@ -1135,7 +2753,7 @@ aria-query@5.3.0:
   dependencies:
     dequal "^2.0.3"
 
-aria-query@^5.3.2:
+aria-query@^5.0.0, aria-query@^5.3.2:
   version "5.3.2"
   resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59"
   integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==
@@ -1229,6 +2847,26 @@ arraybuffer.prototype.slice@^1.0.3:
     is-array-buffer "^3.0.4"
     is-shared-array-buffer "^1.0.2"
 
+asn1.js@^4.10.1:
+  version "4.10.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
+  integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
+  dependencies:
+    bn.js "^4.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+assert@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd"
+  integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==
+  dependencies:
+    call-bind "^1.0.2"
+    is-nan "^1.3.2"
+    object-is "^1.1.5"
+    object.assign "^4.1.4"
+    util "^0.12.5"
+
 assertion-error@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
@@ -1239,6 +2877,13 @@ ast-types-flow@^0.0.8:
   resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6"
   integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==
 
+ast-types@^0.16.1:
+  version "0.16.1"
+  resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.16.1.tgz#7a9da1617c9081bc121faafe91711b4c8bb81da2"
+  integrity sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==
+  dependencies:
+    tslib "^2.0.1"
+
 asynckit@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -1261,11 +2906,48 @@ axobject-query@^4.1.0:
   resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee"
   integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==
 
+babel-loader@^9.1.3:
+  version "9.2.1"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.2.1.tgz#04c7835db16c246dd19ba0914418f3937797587b"
+  integrity sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==
+  dependencies:
+    find-cache-dir "^4.0.0"
+    schema-utils "^4.0.0"
+
+babel-plugin-polyfill-corejs2@^0.4.10:
+  version "0.4.12"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9"
+  integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==
+  dependencies:
+    "@babel/compat-data" "^7.22.6"
+    "@babel/helper-define-polyfill-provider" "^0.6.3"
+    semver "^6.3.1"
+
+babel-plugin-polyfill-corejs3@^0.10.6:
+  version "0.10.6"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7"
+  integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==
+  dependencies:
+    "@babel/helper-define-polyfill-provider" "^0.6.2"
+    core-js-compat "^3.38.0"
+
+babel-plugin-polyfill-regenerator@^0.6.1:
+  version "0.6.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz#abeb1f3f1c762eace37587f42548b08b57789bc8"
+  integrity sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==
+  dependencies:
+    "@babel/helper-define-polyfill-provider" "^0.6.3"
+
 balanced-match@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
   integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 
+base64-js@^1.3.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+  integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
 bcp47@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/bcp47/-/bcp47-1.1.2.tgz#354be3307ffd08433a78f5e1e2095845f89fc7fe"
@@ -1279,11 +2961,38 @@ bcrypt@^5.1.1:
     "@mapbox/node-pre-gyp" "^1.0.11"
     node-addon-api "^5.0.0"
 
+better-opn@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-3.0.2.tgz#f96f35deaaf8f34144a4102651babcf00d1d8817"
+  integrity sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==
+  dependencies:
+    open "^8.0.4"
+
+big.js@^5.2.2:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
+  integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
+
 binary-extensions@^2.0.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
   integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
 
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9:
+  version "4.12.1"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.1.tgz#215741fe3c9dba2d7e12c001d0cfdbae43975ba7"
+  integrity sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==
+
+bn.js@^5.2.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
+  integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
+
+boolbase@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
 brace-expansion@^1.1.7:
   version "1.1.11"
   resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -1306,7 +3015,80 @@ braces@^3.0.3, braces@~3.0.2:
   dependencies:
     fill-range "^7.1.1"
 
-browserslist@^4.24.0:
+brorand@^1.0.1, brorand@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+  integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==
+
+browser-assert@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/browser-assert/-/browser-assert-1.2.1.tgz#9aaa5a2a8c74685c2ae05bfe46efd606f068c200"
+  integrity sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==
+
+browserify-aes@^1.0.4, browserify-aes@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
+  integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
+  dependencies:
+    buffer-xor "^1.0.3"
+    cipher-base "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.3"
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+browserify-cipher@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
+  integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
+  dependencies:
+    browserify-aes "^1.0.4"
+    browserify-des "^1.0.0"
+    evp_bytestokey "^1.0.0"
+
+browserify-des@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
+  integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
+  dependencies:
+    cipher-base "^1.0.1"
+    des.js "^1.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+browserify-rsa@^4.0.0, browserify-rsa@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.1.tgz#06e530907fe2949dc21fc3c2e2302e10b1437238"
+  integrity sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==
+  dependencies:
+    bn.js "^5.2.1"
+    randombytes "^2.1.0"
+    safe-buffer "^5.2.1"
+
+browserify-sign@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.3.tgz#7afe4c01ec7ee59a89a558a4b75bd85ae62d4208"
+  integrity sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==
+  dependencies:
+    bn.js "^5.2.1"
+    browserify-rsa "^4.1.0"
+    create-hash "^1.2.0"
+    create-hmac "^1.1.7"
+    elliptic "^6.5.5"
+    hash-base "~3.0"
+    inherits "^2.0.4"
+    parse-asn1 "^5.1.7"
+    readable-stream "^2.3.8"
+    safe-buffer "^5.2.1"
+
+browserify-zlib@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
+  integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
+  dependencies:
+    pako "~1.0.5"
+
+browserslist@^4.24.0, browserslist@^4.24.2:
   version "4.24.2"
   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580"
   integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==
@@ -1316,6 +3098,29 @@ browserslist@^4.24.0:
     node-releases "^2.0.18"
     update-browserslist-db "^1.1.1"
 
+buffer-from@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+  integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+buffer-xor@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+  integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==
+
+buffer@^6.0.3:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
+  integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.2.1"
+
+builtin-status-codes@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
+  integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==
+
 busboy@1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
@@ -1328,7 +3133,7 @@ cac@^6.7.14:
   resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
   integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
 
-call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
+call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
   integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
@@ -1344,6 +3149,14 @@ callsites@^3.0.0:
   resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
   integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
 
+camel-case@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
+  integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
+  dependencies:
+    pascal-case "^3.1.2"
+    tslib "^2.0.3"
+
 camelcase-css@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
@@ -1354,7 +3167,12 @@ caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001669:
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz#fe133d41fe74af8f7cc93b8a714c3e86a86e6f04"
   integrity sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==
 
-chai@^5.1.2:
+case-sensitive-paths-webpack-plugin@^2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4"
+  integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==
+
+chai@^5.1.1, chai@^5.1.2:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d"
   integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==
@@ -1365,7 +3183,15 @@ chai@^5.1.2:
     loupe "^3.1.0"
     pathval "^2.0.0"
 
-chalk@^4.0.0, chalk@^4.1.0:
+chalk@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+  integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
   integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -1398,6 +3224,29 @@ chownr@^2.0.0:
   resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
   integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
 
+chromatic@^11.15.0:
+  version "11.18.1"
+  resolved "https://registry.yarnpkg.com/chromatic/-/chromatic-11.18.1.tgz#c8f7b330dbd9be3b45a84abcb924f5217dc6c326"
+  integrity sha512-hkNT9vA6K9+PnE/khhZYBnRCOm8NonaQDs7RZ8YHFo7/lh1b/x/uFMkTjWjaj/mkM6QOR/evu5VcZMtcaauSlw==
+
+chrome-trace-event@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b"
+  integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==
+
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.5.tgz#749f80731c7821e9a5fabd51f6998b696f296686"
+  integrity sha512-xq7ICKB4TMHUx7Tz1L9O2SGKOhYMOTR32oir45Bq28/AQTpHogKgHcoYFSdRbMtddl+ozNXfXY9jWcgYKmde0w==
+  dependencies:
+    inherits "^2.0.4"
+    safe-buffer "^5.2.1"
+
+cjs-module-lexer@^1.2.3:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170"
+  integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==
+
 class-variance-authority@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522"
@@ -1405,6 +3254,13 @@ class-variance-authority@^0.7.0:
   dependencies:
     clsx "2.0.0"
 
+clean-css@^5.2.2:
+  version "5.3.3"
+  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd"
+  integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==
+  dependencies:
+    source-map "~0.6.0"
+
 client-only@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
@@ -1453,6 +3309,11 @@ color@^4.2.3:
     color-convert "^2.0.1"
     color-string "^1.9.0"
 
+colorette@^2.0.10:
+  version "2.0.20"
+  resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
+  integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
+
 combined-stream@^1.0.8:
   version "1.0.8"
   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@@ -1460,11 +3321,31 @@ combined-stream@^1.0.8:
   dependencies:
     delayed-stream "~1.0.0"
 
+commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
 commander@^4.0.0:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
   integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
 
+commander@^8.3.0:
+  version "8.3.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
+  integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
+
+common-path-prefix@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
+  integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==
+
+commondir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+  integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
+
 concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -1475,11 +3356,26 @@ confusing-browser-globals@^1.0.10:
   resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81"
   integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==
 
+console-browserify@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
+  integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
+
 console-control-strings@^1.0.0, console-control-strings@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
   integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
 
+constants-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
+  integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==
+
+convert-source-map@^1.7.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
+  integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
+
 convert-source-map@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
@@ -1490,6 +3386,75 @@ cookie@^0.7.2:
   resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
   integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
 
+core-js-compat@^3.38.0, core-js-compat@^3.38.1:
+  version "3.39.0"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.39.0.tgz#b12dccb495f2601dc860bdbe7b4e3ffa8ba63f61"
+  integrity sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==
+  dependencies:
+    browserslist "^4.24.2"
+
+core-js-pure@^3.23.3:
+  version "3.39.0"
+  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.39.0.tgz#aa0d54d70a15bdc13e7c853db87c10abc30d68f3"
+  integrity sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==
+
+core-util-is@~1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+  integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+
+cosmiconfig@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
+  integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
+  dependencies:
+    "@types/parse-json" "^4.0.0"
+    import-fresh "^3.2.1"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+    yaml "^1.10.0"
+
+cosmiconfig@^9.0.0:
+  version "9.0.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d"
+  integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==
+  dependencies:
+    env-paths "^2.2.1"
+    import-fresh "^3.3.0"
+    js-yaml "^4.1.0"
+    parse-json "^5.2.0"
+
+create-ecdh@^4.0.4:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
+  integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==
+  dependencies:
+    bn.js "^4.1.0"
+    elliptic "^6.5.3"
+
+create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
+  integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
+  dependencies:
+    cipher-base "^1.0.1"
+    inherits "^2.0.1"
+    md5.js "^1.3.4"
+    ripemd160 "^2.0.1"
+    sha.js "^2.4.0"
+
+create-hmac@^1.1.4, create-hmac@^1.1.7:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
+  integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
+  dependencies:
+    cipher-base "^1.0.3"
+    create-hash "^1.1.0"
+    inherits "^2.0.1"
+    ripemd160 "^2.0.0"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
 cross-spawn@^7.0.0, cross-spawn@^7.0.2:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -1499,6 +3464,59 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2:
     shebang-command "^2.0.0"
     which "^2.0.1"
 
+crypto-browserify@^3.12.0:
+  version "3.12.1"
+  resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.1.tgz#bb8921bec9acc81633379aa8f52d69b0b69e0dac"
+  integrity sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==
+  dependencies:
+    browserify-cipher "^1.0.1"
+    browserify-sign "^4.2.3"
+    create-ecdh "^4.0.4"
+    create-hash "^1.2.0"
+    create-hmac "^1.1.7"
+    diffie-hellman "^5.0.3"
+    hash-base "~3.0.4"
+    inherits "^2.0.4"
+    pbkdf2 "^3.1.2"
+    public-encrypt "^4.0.3"
+    randombytes "^2.1.0"
+    randomfill "^1.0.4"
+
+css-loader@^6.7.1, css-loader@^6.7.3:
+  version "6.11.0"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba"
+  integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==
+  dependencies:
+    icss-utils "^5.1.0"
+    postcss "^8.4.33"
+    postcss-modules-extract-imports "^3.1.0"
+    postcss-modules-local-by-default "^4.0.5"
+    postcss-modules-scope "^3.2.0"
+    postcss-modules-values "^4.0.0"
+    postcss-value-parser "^4.2.0"
+    semver "^7.5.4"
+
+css-select@^4.1.3:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
+  integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^6.0.1"
+    domhandler "^4.3.1"
+    domutils "^2.8.0"
+    nth-check "^2.0.1"
+
+css-what@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+  integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
+css.escape@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
+  integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
+
 cssesc@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@@ -1556,7 +3574,7 @@ data-view-byte-offset@^1.0.0:
     es-errors "^1.3.0"
     is-data-view "^1.0.1"
 
-debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7:
+debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7:
   version "4.3.7"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
   integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
@@ -1575,6 +3593,11 @@ decimal.js@^10.4.3:
   resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
   integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
 
+dedent@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
+  integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==
+
 deep-eql@^5.0.1:
   version "5.0.2"
   resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
@@ -1585,6 +3608,11 @@ deep-is@^0.1.3:
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
   integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
 
+deepmerge@^4.2.2:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+  integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
+
 define-data-property@^1.0.1, define-data-property@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
@@ -1594,6 +3622,11 @@ define-data-property@^1.0.1, define-data-property@^1.1.4:
     es-errors "^1.3.0"
     gopd "^1.0.1"
 
+define-lazy-prop@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
+  integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
+
 define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
@@ -1613,11 +3646,19 @@ delegates@^1.0.0:
   resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
   integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
 
-dequal@^2.0.3:
+dequal@^2.0.2, dequal@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
   integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
 
+des.js@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da"
+  integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==
+  dependencies:
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
 detect-libc@^2.0.0, detect-libc@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
@@ -1628,6 +3669,15 @@ didyoumean@^1.2.2:
   resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
   integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
 
+diffie-hellman@^5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
+  integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
+  dependencies:
+    bn.js "^4.1.0"
+    miller-rabin "^4.0.0"
+    randombytes "^2.0.0"
+
 dlv@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
@@ -1652,6 +3702,61 @@ dom-accessibility-api@^0.5.9:
   resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
   integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
 
+dom-accessibility-api@^0.6.3:
+  version "0.6.3"
+  resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8"
+  integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==
+
+dom-converter@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
+  integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==
+  dependencies:
+    utila "~0.4"
+
+dom-serializer@^1.0.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
+  integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.0"
+    entities "^2.0.0"
+
+domain-browser@^4.22.0:
+  version "4.23.0"
+  resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.23.0.tgz#427ebb91efcb070f05cffdfb8a4e9a6c25f8c94b"
+  integrity sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==
+
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+  integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
+  integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
+  dependencies:
+    domelementtype "^2.2.0"
+
+domutils@^2.5.2, domutils@^2.8.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+  integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
+  dependencies:
+    dom-serializer "^1.0.1"
+    domelementtype "^2.2.0"
+    domhandler "^4.2.0"
+
+dot-case@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
+  integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
+  dependencies:
+    no-case "^3.0.4"
+    tslib "^2.0.3"
+
 dotenv@^16.4.5:
   version "16.4.5"
   resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
@@ -1667,6 +3772,19 @@ electron-to-chromium@^1.5.41:
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz#d9ba818da7b2b5ef1f3dd32bce7046feb7e93234"
   integrity sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==
 
+elliptic@^6.5.3, elliptic@^6.5.5:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06"
+  integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==
+  dependencies:
+    bn.js "^4.11.9"
+    brorand "^1.1.0"
+    hash.js "^1.0.0"
+    hmac-drbg "^1.0.1"
+    inherits "^2.0.4"
+    minimalistic-assert "^1.0.1"
+    minimalistic-crypto-utils "^1.0.1"
+
 emoji-regex@^8.0.0:
   version "8.0.0"
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@@ -1677,7 +3795,21 @@ emoji-regex@^9.2.2:
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
   integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
 
-enhanced-resolve@^5.15.0:
+emojis-list@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
+  integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
+
+endent@^2.0.1:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/endent/-/endent-2.1.0.tgz#5aaba698fb569e5e18e69e1ff7a28ff35373cd88"
+  integrity sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==
+  dependencies:
+    dedent "^0.7.0"
+    fast-json-parse "^1.0.3"
+    objectorarray "^1.0.5"
+
+enhanced-resolve@^5.15.0, enhanced-resolve@^5.17.1, enhanced-resolve@^5.7.0:
   version "5.17.1"
   resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15"
   integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==
@@ -1685,11 +3817,35 @@ enhanced-resolve@^5.15.0:
     graceful-fs "^4.2.4"
     tapable "^2.2.0"
 
+entities@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+  integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
 entities@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
   integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
 
+env-paths@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
+  integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+error-stack-parser@^2.0.6:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
+  integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==
+  dependencies:
+    stackframe "^1.3.4"
+
 es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3:
   version "1.23.3"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0"
@@ -1774,6 +3930,11 @@ es-iterator-helpers@^1.1.0:
     iterator.prototype "^1.1.3"
     safe-array-concat "^1.1.2"
 
+es-module-lexer@^1.2.1, es-module-lexer@^1.5.0:
+  version "1.5.4"
+  resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78"
+  integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==
+
 es-object-atoms@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941"
@@ -1806,6 +3967,43 @@ es-to-primitive@^1.2.1:
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
+esbuild-register@^3.5.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/esbuild-register/-/esbuild-register-3.6.0.tgz#cf270cfa677baebbc0010ac024b823cbf723a36d"
+  integrity sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==
+  dependencies:
+    debug "^4.3.4"
+
+"esbuild@^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0":
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.0.tgz#f2d470596885fcb2e91c21eb3da3b3c89c0b55e7"
+  integrity sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==
+  optionalDependencies:
+    "@esbuild/aix-ppc64" "0.24.0"
+    "@esbuild/android-arm" "0.24.0"
+    "@esbuild/android-arm64" "0.24.0"
+    "@esbuild/android-x64" "0.24.0"
+    "@esbuild/darwin-arm64" "0.24.0"
+    "@esbuild/darwin-x64" "0.24.0"
+    "@esbuild/freebsd-arm64" "0.24.0"
+    "@esbuild/freebsd-x64" "0.24.0"
+    "@esbuild/linux-arm" "0.24.0"
+    "@esbuild/linux-arm64" "0.24.0"
+    "@esbuild/linux-ia32" "0.24.0"
+    "@esbuild/linux-loong64" "0.24.0"
+    "@esbuild/linux-mips64el" "0.24.0"
+    "@esbuild/linux-ppc64" "0.24.0"
+    "@esbuild/linux-riscv64" "0.24.0"
+    "@esbuild/linux-s390x" "0.24.0"
+    "@esbuild/linux-x64" "0.24.0"
+    "@esbuild/netbsd-x64" "0.24.0"
+    "@esbuild/openbsd-arm64" "0.24.0"
+    "@esbuild/openbsd-x64" "0.24.0"
+    "@esbuild/sunos-x64" "0.24.0"
+    "@esbuild/win32-arm64" "0.24.0"
+    "@esbuild/win32-ia32" "0.24.0"
+    "@esbuild/win32-x64" "0.24.0"
+
 esbuild@^0.21.3:
   version "0.21.5"
   resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
@@ -2003,6 +4201,23 @@ eslint-plugin-react@^7.35.0:
     string.prototype.matchall "^4.0.11"
     string.prototype.repeat "^1.0.0"
 
+eslint-plugin-storybook@^0.11.1:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.11.1.tgz#4ef4f3550855fdc4a902296dfc278340ec287506"
+  integrity sha512-yGKpAYkBm/Q2hZg476vRUAvd9lAccjjSvzU5nYy3BSQbKTPy7uopx7JEpwk2vSuw4weTMZzWF64z9/gp/K5RCg==
+  dependencies:
+    "@storybook/csf" "^0.1.11"
+    "@typescript-eslint/utils" "^8.8.1"
+    ts-dedent "^2.2.0"
+
+eslint-scope@5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+  integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^4.1.1"
+
 eslint-scope@^7.2.2:
   version "7.2.2"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
@@ -2016,6 +4231,11 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
   integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
 
+eslint-visitor-keys@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
+  integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
+
 eslint@^8:
   version "8.57.1"
   resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9"
@@ -2069,6 +4289,11 @@ espree@^9.6.0, espree@^9.6.1:
     acorn-jsx "^5.3.2"
     eslint-visitor-keys "^3.4.1"
 
+esprima@~4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
 esquery@^1.4.2:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
@@ -2083,6 +4308,11 @@ esrecurse@^4.3.0:
   dependencies:
     estraverse "^5.2.0"
 
+estraverse@^4.1.1:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
 estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
@@ -2100,6 +4330,24 @@ esutils@^2.0.2:
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
   integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
 
+event-target-shim@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
+  integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
+
+events@^3.2.0, events@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
+  integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
+evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
+  integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
+  dependencies:
+    md5.js "^1.3.4"
+    safe-buffer "^5.1.1"
+
 expect-type@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75"
@@ -2137,6 +4385,11 @@ fast-glob@^3.3.0, fast-glob@^3.3.2:
     merge2 "^1.3.0"
     micromatch "^4.0.4"
 
+fast-json-parse@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d"
+  integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==
+
 fast-json-stable-stringify@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -2147,6 +4400,11 @@ fast-levenshtein@^2.0.6:
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
   integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
 
+fast-uri@^3.0.1:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241"
+  integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==
+
 fastq@^1.6.0:
   version "1.17.1"
   resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
@@ -2161,6 +4419,11 @@ file-entry-cache@^6.0.1:
   dependencies:
     flat-cache "^3.0.4"
 
+filesize@^10.0.12:
+  version "10.1.6"
+  resolved "https://registry.yarnpkg.com/filesize/-/filesize-10.1.6.tgz#31194da825ac58689c0bce3948f33ce83aabd361"
+  integrity sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==
+
 fill-range@^7.1.1:
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
@@ -2168,6 +4431,36 @@ fill-range@^7.1.1:
   dependencies:
     to-regex-range "^5.0.1"
 
+filter-obj@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-2.0.2.tgz#fff662368e505d69826abb113f0f6a98f56e9d5f"
+  integrity sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==
+
+find-cache-dir@^3.3.1:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b"
+  integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==
+  dependencies:
+    commondir "^1.0.1"
+    make-dir "^3.0.2"
+    pkg-dir "^4.1.0"
+
+find-cache-dir@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2"
+  integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==
+  dependencies:
+    common-path-prefix "^3.0.0"
+    pkg-dir "^7.0.0"
+
+find-up@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+  integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+  dependencies:
+    locate-path "^5.0.0"
+    path-exists "^4.0.0"
+
 find-up@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
@@ -2176,6 +4469,14 @@ find-up@^5.0.0:
     locate-path "^6.0.0"
     path-exists "^4.0.0"
 
+find-up@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790"
+  integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==
+  dependencies:
+    locate-path "^7.1.0"
+    path-exists "^5.0.0"
+
 flat-cache@^3.0.4:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
@@ -2205,6 +4506,24 @@ foreground-child@^3.1.0:
     cross-spawn "^7.0.0"
     signal-exit "^4.0.1"
 
+fork-ts-checker-webpack-plugin@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz#dae45dfe7298aa5d553e2580096ced79b6179504"
+  integrity sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==
+  dependencies:
+    "@babel/code-frame" "^7.16.7"
+    chalk "^4.1.2"
+    chokidar "^3.5.3"
+    cosmiconfig "^7.0.1"
+    deepmerge "^4.2.2"
+    fs-extra "^10.0.0"
+    memfs "^3.4.1"
+    minimatch "^3.0.4"
+    node-abort-controller "^3.0.1"
+    schema-utils "^3.1.1"
+    semver "^7.3.5"
+    tapable "^2.2.1"
+
 form-data@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48"
@@ -2219,6 +4538,15 @@ fp-ts@^2.16.9:
   resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.16.9.tgz#99628fc5e0bb3b432c4a16d8f4455247380bae8a"
   integrity sha512-+I2+FnVB+tVaxcYyQkHUq7ZdKScaBlX53A41mxQtpIccsfyv8PzdzP7fzp2AY832T4aoK6UZ5WRX/ebGd8uZuQ==
 
+fs-extra@^10.0.0:
+  version "10.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
+  integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^6.0.1"
+    universalify "^2.0.0"
+
 fs-minipass@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@@ -2226,6 +4554,11 @@ fs-minipass@^2.0.0:
   dependencies:
     minipass "^3.0.0"
 
+fs-monkey@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2"
+  integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==
+
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -2317,6 +4650,11 @@ glob-parent@^6.0.2:
   dependencies:
     is-glob "^4.0.3"
 
+glob-to-regexp@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+  integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
 glob@^10.3.10:
   version "10.4.5"
   resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
@@ -2368,7 +4706,7 @@ gopd@^1.0.1:
   dependencies:
     get-intrinsic "^1.1.3"
 
-graceful-fs@^4.2.4:
+graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4:
   version "4.2.11"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
   integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -2417,6 +4755,31 @@ has-unicode@^2.0.1:
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
   integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
 
+hash-base@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33"
+  integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==
+  dependencies:
+    inherits "^2.0.4"
+    readable-stream "^3.6.0"
+    safe-buffer "^5.2.0"
+
+hash-base@~3.0, hash-base@~3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
+  integrity sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+hash.js@^1.0.0, hash.js@^1.0.3:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+  integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
+  dependencies:
+    inherits "^2.0.3"
+    minimalistic-assert "^1.0.1"
+
 hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
@@ -2424,6 +4787,20 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
   dependencies:
     function-bind "^1.1.2"
 
+he@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+hmac-drbg@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+  integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==
+  dependencies:
+    hash.js "^1.0.3"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.1"
+
 hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
@@ -2438,6 +4815,24 @@ html-encoding-sniffer@^4.0.0:
   dependencies:
     whatwg-encoding "^3.1.1"
 
+html-entities@^2.1.0:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f"
+  integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==
+
+html-minifier-terser@^6.0.2:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab"
+  integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==
+  dependencies:
+    camel-case "^4.1.2"
+    clean-css "^5.2.2"
+    commander "^8.3.0"
+    he "^1.2.0"
+    param-case "^3.0.4"
+    relateurl "^0.2.7"
+    terser "^5.10.0"
+
 html-parse-stringify@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
@@ -2445,6 +4840,27 @@ html-parse-stringify@^3.0.1:
   dependencies:
     void-elements "3.1.0"
 
+html-webpack-plugin@^5.5.0:
+  version "5.6.3"
+  resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz#a31145f0fee4184d53a794f9513147df1e653685"
+  integrity sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==
+  dependencies:
+    "@types/html-minifier-terser" "^6.0.0"
+    html-minifier-terser "^6.0.2"
+    lodash "^4.17.21"
+    pretty-error "^4.0.0"
+    tapable "^2.0.0"
+
+htmlparser2@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
+  integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.0.0"
+    domutils "^2.5.2"
+    entities "^2.0.0"
+
 http-proxy-agent@^7.0.2:
   version "7.0.2"
   resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
@@ -2453,6 +4869,11 @@ http-proxy-agent@^7.0.2:
     agent-base "^7.1.0"
     debug "^4.3.4"
 
+https-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
+  integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==
+
 https-proxy-agent@^5.0.0:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
@@ -2497,12 +4918,29 @@ iconv-lite@0.6.3:
   dependencies:
     safer-buffer ">= 2.1.2 < 3.0.0"
 
+icss-utils@^5.0.0, icss-utils@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
+  integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
+
+ieee754@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+  integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
 ignore@^5.2.0, ignore@^5.3.1:
   version "5.3.2"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
   integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
 
-import-fresh@^3.2.1:
+image-size@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.1.1.tgz#ddd67d4dc340e52ac29ce5f546a09f4e29e840ac"
+  integrity sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==
+  dependencies:
+    queue "6.0.2"
+
+import-fresh@^3.2.1, import-fresh@^3.3.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
   integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
@@ -2515,6 +4953,11 @@ imurmurhash@^0.1.4:
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
   integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
 
+indent-string@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
+  integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
+
 inflight@^1.0.4:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -2523,7 +4966,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@^2.0.3:
+inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -2537,6 +4980,14 @@ internal-slot@^1.0.7:
     hasown "^2.0.0"
     side-channel "^1.0.4"
 
+is-arguments@^1.0.4:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
+  integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
+  dependencies:
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
+
 is-array-buffer@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98"
@@ -2545,6 +4996,11 @@ is-array-buffer@^3.0.4:
     call-bind "^1.0.2"
     get-intrinsic "^1.2.1"
 
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
 is-arrayish@^0.3.1:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
@@ -2612,6 +5068,11 @@ is-date-object@^1.0.1, is-date-object@^1.0.5:
   dependencies:
     has-tostringtag "^1.0.0"
 
+is-docker@^2.0.0, is-docker@^2.1.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
+  integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+
 is-extglob@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -2629,7 +5090,7 @@ is-fullwidth-code-point@^3.0.0:
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
   integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
 
-is-generator-function@^1.0.10:
+is-generator-function@^1.0.10, is-generator-function@^1.0.7:
   version "1.0.10"
   resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
   integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
@@ -2648,6 +5109,14 @@ is-map@^2.0.3:
   resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
   integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
 
+is-nan@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
+  integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
+  dependencies:
+    call-bind "^1.0.0"
+    define-properties "^1.1.3"
+
 is-negative-zero@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747"
@@ -2709,7 +5178,7 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
   dependencies:
     has-symbols "^1.0.2"
 
-is-typed-array@^1.1.13:
+is-typed-array@^1.1.13, is-typed-array@^1.1.3:
   version "1.1.13"
   resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229"
   integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==
@@ -2736,11 +5205,23 @@ is-weakset@^2.0.3:
     call-bind "^1.0.7"
     get-intrinsic "^1.2.4"
 
+is-wsl@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
+  integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+  dependencies:
+    is-docker "^2.0.0"
+
 isarray@^2.0.5:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
   integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
 
+isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
+
 isexe@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -2766,7 +5247,16 @@ jackspeak@^3.1.2:
   optionalDependencies:
     "@pkgjs/parseargs" "^0.11.0"
 
-jiti@^1.21.0:
+jest-worker@^27.4.5:
+  version "27.5.1"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
+  integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
+  dependencies:
+    "@types/node" "*"
+    merge-stream "^2.0.0"
+    supports-color "^8.0.0"
+
+jiti@^1.20.0, jiti@^1.21.0:
   version "1.21.6"
   resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268"
   integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
@@ -2783,6 +5273,11 @@ js-yaml@^4.1.0:
   dependencies:
     argparse "^2.0.1"
 
+jsdoc-type-pratt-parser@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz#ff6b4a3f339c34a6c188cbf50a16087858d22113"
+  integrity sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==
+
 jsdom@^25.0.1:
   version "25.0.1"
   resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-25.0.1.tgz#536ec685c288fc8a5773a65f82d8b44badcc73ef"
@@ -2810,7 +5305,7 @@ jsdom@^25.0.1:
     ws "^8.18.0"
     xml-name-validator "^5.0.0"
 
-jsesc@^3.0.2:
+jsesc@^3.0.2, jsesc@~3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e"
   integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==
@@ -2820,11 +5315,21 @@ json-buffer@3.0.1:
   resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
   integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
 
+json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
 json-schema-traverse@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
   integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
 
+json-schema-traverse@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+  integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
 json-stable-stringify-without-jsonify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@@ -2837,11 +5342,20 @@ json5@^1.0.2:
   dependencies:
     minimist "^1.2.0"
 
-json5@^2.2.3:
+json5@^2.1.2, json5@^2.2.2, json5@^2.2.3:
   version "2.2.3"
   resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
   integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
 
+jsonfile@^6.0.1, jsonfile@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+  integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+  dependencies:
+    universalify "^2.0.0"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
 "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5:
   version "3.3.5"
   resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
@@ -2894,6 +5408,32 @@ lines-and-columns@^1.1.6:
   resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
   integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
 
+loader-runner@^4.2.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
+  integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
+
+loader-utils@^2.0.0, loader-utils@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
+  integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
+  dependencies:
+    big.js "^5.2.2"
+    emojis-list "^3.0.0"
+    json5 "^2.1.2"
+
+loader-utils@^3.2.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.3.1.tgz#735b9a19fd63648ca7adbd31c2327dfe281304e5"
+  integrity sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==
+
+locate-path@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+  integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+  dependencies:
+    p-locate "^4.1.0"
+
 locate-path@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
@@ -2901,23 +5441,47 @@ locate-path@^6.0.0:
   dependencies:
     p-locate "^5.0.0"
 
+locate-path@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a"
+  integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==
+  dependencies:
+    p-locate "^6.0.0"
+
+lodash.debounce@^4.0.8:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+  integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+
 lodash.merge@^4.6.2:
   version "4.6.2"
   resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
   integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
 
-loose-envify@^1.4.0:
+lodash@^4.17.20, lodash@^4.17.21:
+  version "4.17.21"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+loose-envify@^1.1.0, loose-envify@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
   integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
   dependencies:
     js-tokens "^3.0.0 || ^4.0.0"
 
-loupe@^3.1.0, loupe@^3.1.2:
+loupe@^3.1.0, loupe@^3.1.1, loupe@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240"
   integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==
 
+lower-case@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
+  integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
+  dependencies:
+    tslib "^2.0.3"
+
 lru-cache@^10.2.0:
   version "10.4.3"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
@@ -2947,19 +5511,59 @@ magic-string@^0.30.12:
   dependencies:
     "@jridgewell/sourcemap-codec" "^1.5.0"
 
-make-dir@^3.1.0:
+magic-string@^0.30.5:
+  version "0.30.13"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.13.tgz#92438e3ff4946cf54f18247c981e5c161c46683c"
+  integrity sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+
+make-dir@^3.0.2, make-dir@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
   integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
   dependencies:
     semver "^6.0.0"
 
+map-or-similar@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08"
+  integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==
+
+md5.js@^1.3.4:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
+  integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+memfs@^3.4.1, memfs@^3.4.12:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6"
+  integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ==
+  dependencies:
+    fs-monkey "^1.0.4"
+
+memoizerific@^1.11.3:
+  version "1.11.3"
+  resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a"
+  integrity sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==
+  dependencies:
+    map-or-similar "^1.5.0"
+
+merge-stream@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+  integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
 merge2@^1.3.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
   integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
 
-micromatch@^4.0.4, micromatch@^4.0.5:
+micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
   integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
@@ -2967,19 +5571,42 @@ micromatch@^4.0.4, micromatch@^4.0.5:
     braces "^3.0.3"
     picomatch "^2.3.1"
 
+miller-rabin@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
+  integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
+  dependencies:
+    bn.js "^4.0.0"
+    brorand "^1.0.1"
+
 mime-db@1.52.0:
   version "1.52.0"
   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
   integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
 
-mime-types@^2.1.12:
+mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31:
   version "2.1.35"
   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
   integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
   dependencies:
     mime-db "1.52.0"
 
-minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+min-indent@^1.0.0, min-indent@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
+  integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
+
+minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+  integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
+
+minimalistic-crypto-utils@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+  integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==
+
+minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
   integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -3064,6 +5691,11 @@ negotiator@^0.6.3:
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7"
   integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==
 
+neo-async@^2.6.2:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+  integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
 next-i18n-router@^5.5.1:
   version "5.5.1"
   resolved "https://registry.yarnpkg.com/next-i18n-router/-/next-i18n-router-5.5.1.tgz#3d4c34d431e38aa4ba8f52cd54d4387fc70db6d2"
@@ -3072,6 +5704,11 @@ next-i18n-router@^5.5.1:
     "@formatjs/intl-localematcher" "^0.5.2"
     negotiator "^0.6.3"
 
+next-themes@^0.4.3:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.3.tgz#ea54552d5986936d177eed393ea50b658ae44800"
+  integrity sha512-nG84VPkTdUHR2YeD89YchvV4I9RbiMAql3GiLEQlPvq1ioaqPaIReK+yMRdg/zgiXws620qS1rU30TiWmmG9lA==
+
 next@15.0.2:
   version "15.0.2"
   resolved "https://registry.yarnpkg.com/next/-/next-15.0.2.tgz#4a2224c007856118010b8cef5e9b2383cd743388"
@@ -3095,6 +5732,19 @@ next@15.0.2:
     "@next/swc-win32-x64-msvc" "15.0.2"
     sharp "^0.33.5"
 
+no-case@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
+  integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
+  dependencies:
+    lower-case "^2.0.2"
+    tslib "^2.0.3"
+
+node-abort-controller@^3.0.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548"
+  integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==
+
 node-addon-api@^5.0.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
@@ -3107,6 +5757,37 @@ node-fetch@^2.6.7:
   dependencies:
     whatwg-url "^5.0.0"
 
+node-polyfill-webpack-plugin@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-2.0.1.tgz#141d86f177103a8517c71d99b7c6a46edbb1bb58"
+  integrity sha512-ZUMiCnZkP1LF0Th2caY6J/eKKoA0TefpoVa68m/LQU1I/mE8rGt4fNYGgNuCcK+aG8P8P43nbeJ2RqJMOL/Y1A==
+  dependencies:
+    assert "^2.0.0"
+    browserify-zlib "^0.2.0"
+    buffer "^6.0.3"
+    console-browserify "^1.2.0"
+    constants-browserify "^1.0.0"
+    crypto-browserify "^3.12.0"
+    domain-browser "^4.22.0"
+    events "^3.3.0"
+    filter-obj "^2.0.2"
+    https-browserify "^1.0.0"
+    os-browserify "^0.3.0"
+    path-browserify "^1.0.1"
+    process "^0.11.10"
+    punycode "^2.1.1"
+    querystring-es3 "^0.2.1"
+    readable-stream "^4.0.0"
+    stream-browserify "^3.0.0"
+    stream-http "^3.2.0"
+    string_decoder "^1.3.0"
+    timers-browserify "^2.0.12"
+    tty-browserify "^0.0.1"
+    type-fest "^2.14.0"
+    url "^0.11.0"
+    util "^0.12.4"
+    vm-browserify "^1.1.2"
+
 node-releases@^2.0.18:
   version "2.0.18"
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
@@ -3134,6 +5815,13 @@ npmlog@^5.0.1:
     gauge "^3.0.0"
     set-blocking "^2.0.0"
 
+nth-check@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+  dependencies:
+    boolbase "^1.0.0"
+
 nwsapi@^2.2.12:
   version "2.2.13"
   resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.13.tgz#e56b4e98960e7a040e5474536587e599c4ff4655"
@@ -3154,6 +5842,14 @@ object-inspect@^1.13.1:
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
   integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
 
+object-is@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07"
+  integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==
+  dependencies:
+    call-bind "^1.0.7"
+    define-properties "^1.2.1"
+
 object-keys@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
@@ -3206,6 +5902,11 @@ object.values@^1.1.6, object.values@^1.2.0:
     define-properties "^1.2.1"
     es-object-atoms "^1.0.0"
 
+objectorarray@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5"
+  integrity sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==
+
 once@^1.3.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -3213,6 +5914,15 @@ once@^1.3.0:
   dependencies:
     wrappy "1"
 
+open@^8.0.4:
+  version "8.4.2"
+  resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9"
+  integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==
+  dependencies:
+    define-lazy-prop "^2.0.0"
+    is-docker "^2.1.1"
+    is-wsl "^2.2.0"
+
 optionator@^0.9.3:
   version "0.9.4"
   resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
@@ -3225,6 +5935,18 @@ optionator@^0.9.3:
     type-check "^0.4.0"
     word-wrap "^1.2.5"
 
+os-browserify@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
+  integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==
+
+p-limit@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+  integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+  dependencies:
+    p-try "^2.0.0"
+
 p-limit@^3.0.2:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
@@ -3232,6 +5954,20 @@ p-limit@^3.0.2:
   dependencies:
     yocto-queue "^0.1.0"
 
+p-limit@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644"
+  integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==
+  dependencies:
+    yocto-queue "^1.0.0"
+
+p-locate@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+  integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+  dependencies:
+    p-limit "^2.2.0"
+
 p-locate@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
@@ -3239,11 +5975,36 @@ p-locate@^5.0.0:
   dependencies:
     p-limit "^3.0.2"
 
+p-locate@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f"
+  integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==
+  dependencies:
+    p-limit "^4.0.0"
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
 package-json-from-dist@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
   integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
 
+pako@~1.0.5:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
+  integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
+
+param-case@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
+  integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
+  dependencies:
+    dot-case "^3.0.4"
+    tslib "^2.0.3"
+
 parent-module@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -3251,6 +6012,28 @@ parent-module@^1.0.0:
   dependencies:
     callsites "^3.0.0"
 
+parse-asn1@^5.0.0, parse-asn1@^5.1.7:
+  version "5.1.7"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.7.tgz#73cdaaa822125f9647165625eb45f8a051d2df06"
+  integrity sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==
+  dependencies:
+    asn1.js "^4.10.1"
+    browserify-aes "^1.2.0"
+    evp_bytestokey "^1.0.3"
+    hash-base "~3.0"
+    pbkdf2 "^3.1.2"
+    safe-buffer "^5.2.1"
+
+parse-json@^5.0.0, parse-json@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+  integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
 parse5@^7.1.2:
   version "7.2.1"
   resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a"
@@ -3258,11 +6041,29 @@ parse5@^7.1.2:
   dependencies:
     entities "^4.5.0"
 
+pascal-case@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
+  integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
+  dependencies:
+    no-case "^3.0.4"
+    tslib "^2.0.3"
+
+path-browserify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
+  integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
+
 path-exists@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
   integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
 
+path-exists@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7"
+  integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==
+
 path-is-absolute@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -3286,6 +6087,11 @@ path-scurry@^1.11.1:
     lru-cache "^10.2.0"
     minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
 
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
 pathe@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
@@ -3296,7 +6102,18 @@ pathval@^2.0.0:
   resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25"
   integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==
 
-picocolors@^1.0.0, picocolors@^1.1.0:
+pbkdf2@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
+  integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
+  dependencies:
+    create-hash "^1.1.2"
+    create-hmac "^1.1.4"
+    ripemd160 "^2.0.1"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
   integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
@@ -3316,6 +6133,34 @@ pirates@^4.0.1:
   resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
   integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
 
+pkg-dir@^4.1.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+  integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+  dependencies:
+    find-up "^4.0.0"
+
+pkg-dir@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11"
+  integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==
+  dependencies:
+    find-up "^6.3.0"
+
+pnp-webpack-plugin@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.7.0.tgz#65741384f6d8056f36e2255a8d67ffc20866f5c9"
+  integrity sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==
+  dependencies:
+    ts-pnp "^1.1.6"
+
+polished@^4.2.2:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/polished/-/polished-4.3.1.tgz#5a00ae32715609f83d89f6f31d0f0261c6170548"
+  integrity sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==
+  dependencies:
+    "@babel/runtime" "^7.17.8"
+
 possible-typed-array-names@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
@@ -3345,6 +6190,43 @@ postcss-load-config@^4.0.1:
     lilconfig "^3.0.0"
     yaml "^2.3.4"
 
+postcss-loader@^8.1.1:
+  version "8.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-8.1.1.tgz#2822589e7522927344954acb55bbf26e8b195dfe"
+  integrity sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==
+  dependencies:
+    cosmiconfig "^9.0.0"
+    jiti "^1.20.0"
+    semver "^7.5.4"
+
+postcss-modules-extract-imports@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002"
+  integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==
+
+postcss-modules-local-by-default@^4.0.5:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.1.0.tgz#b0db6bc81ffc7bdc52eb0f84d6ca0bedf0e36d21"
+  integrity sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q==
+  dependencies:
+    icss-utils "^5.0.0"
+    postcss-selector-parser "^7.0.0"
+    postcss-value-parser "^4.1.0"
+
+postcss-modules-scope@^3.2.0:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz#1bbccddcb398f1d7a511e0a2d1d047718af4078c"
+  integrity sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==
+  dependencies:
+    postcss-selector-parser "^7.0.0"
+
+postcss-modules-values@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
+  integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
+  dependencies:
+    icss-utils "^5.0.0"
+
 postcss-nested@^6.0.1:
   version "6.2.0"
   resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131"
@@ -3360,7 +6242,15 @@ postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.1.1:
     cssesc "^3.0.0"
     util-deprecate "^1.0.2"
 
-postcss-value-parser@^4.0.0:
+postcss-selector-parser@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz#41bd8b56f177c093ca49435f65731befe25d6b9c"
+  integrity sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==
+  dependencies:
+    cssesc "^3.0.0"
+    util-deprecate "^1.0.2"
+
+postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
   integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
@@ -3383,6 +6273,15 @@ postcss@^8, postcss@^8.4.23, postcss@^8.4.43:
     picocolors "^1.1.0"
     source-map-js "^1.2.1"
 
+postcss@^8.2.14, postcss@^8.4.33, postcss@^8.4.38:
+  version "8.4.49"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19"
+  integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==
+  dependencies:
+    nanoid "^3.3.7"
+    picocolors "^1.1.1"
+    source-map-js "^1.2.1"
+
 postgres@^3.4.5:
   version "3.4.5"
   resolved "https://registry.yarnpkg.com/postgres/-/postgres-3.4.5.tgz#1ef99e51b0ba9b53cbda8a215dd406725f7d15f9"
@@ -3405,6 +6304,14 @@ prettier@^3.3.3:
   resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
   integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
 
+pretty-error@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6"
+  integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==
+  dependencies:
+    lodash "^4.17.20"
+    renderkid "^3.0.0"
+
 pretty-format@^27.0.2:
   version "27.5.1"
   resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
@@ -3414,6 +6321,16 @@ pretty-format@^27.0.2:
     ansi-styles "^5.0.0"
     react-is "^17.0.1"
 
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+  integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
+
 prop-types@^15.8.1:
   version "15.8.1"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
@@ -3423,16 +6340,79 @@ prop-types@^15.8.1:
     object-assign "^4.1.1"
     react-is "^16.13.1"
 
-punycode@^2.1.0, punycode@^2.3.1:
+public-encrypt@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
+  integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
+  dependencies:
+    bn.js "^4.1.0"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    parse-asn1 "^5.0.0"
+    randombytes "^2.0.1"
+    safe-buffer "^5.1.2"
+
+punycode@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+  integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==
+
+punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
   integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
 
+qs@^6.12.3:
+  version "6.13.1"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e"
+  integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==
+  dependencies:
+    side-channel "^1.0.6"
+
+querystring-es3@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
+  integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==
+
 queue-microtask@^1.2.2:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
   integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
 
+queue@6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65"
+  integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==
+  dependencies:
+    inherits "~2.0.3"
+
+randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+  dependencies:
+    safe-buffer "^5.1.0"
+
+randomfill@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
+  integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
+  dependencies:
+    randombytes "^2.0.5"
+    safe-buffer "^5.1.0"
+
+range-parser@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+react-confetti@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/react-confetti/-/react-confetti-6.1.0.tgz#03dc4340d955acd10b174dbf301f374a06e29ce6"
+  integrity sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==
+  dependencies:
+    tween-functions "^1.2.0"
+
 react-cookie@^7.2.2:
   version "7.2.2"
   resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.2.2.tgz#a7559e552ea9cca39a4b3686723a5acf504b8f84"
@@ -3442,6 +6422,27 @@ react-cookie@^7.2.2:
     hoist-non-react-statics "^3.3.2"
     universal-cookie "^7.0.0"
 
+react-docgen-typescript@^2.2.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz#4611055e569edc071204aadb20e1c93e1ab1659c"
+  integrity sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==
+
+react-docgen@^7.0.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-7.1.0.tgz#4b41e557dab939a5157be09ee532fd09c07d99fc"
+  integrity sha512-APPU8HB2uZnpl6Vt/+0AFoVYgSRtfiP6FLrZgPPTDmqSb2R4qZRbgd0A3VzIFxDt5e+Fozjx79WjLWnF69DK8g==
+  dependencies:
+    "@babel/core" "^7.18.9"
+    "@babel/traverse" "^7.18.9"
+    "@babel/types" "^7.18.9"
+    "@types/babel__core" "^7.18.0"
+    "@types/babel__traverse" "^7.18.0"
+    "@types/doctrine" "^0.0.9"
+    "@types/resolve" "^1.20.2"
+    doctrine "^3.0.0"
+    resolve "^1.22.1"
+    strip-indent "^4.0.0"
+
 react-dom@19.0.0-rc-69d4b800-20241021:
   version "19.0.0-rc-69d4b800-20241021"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0-rc-69d4b800-20241021.tgz#e27b4f2c962236e9ece496a0ea1c9c7161608ea0"
@@ -3449,6 +6450,14 @@ react-dom@19.0.0-rc-69d4b800-20241021:
   dependencies:
     scheduler "0.25.0-rc-69d4b800-20241021"
 
+"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0":
+  version "18.3.1"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4"
+  integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
+  dependencies:
+    loose-envify "^1.1.0"
+    scheduler "^0.23.2"
+
 react-i18next@^15.1.0:
   version "15.1.0"
   resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.1.0.tgz#9494e4add2389f04c205dd7628c1aa75747b98a3"
@@ -3467,7 +6476,7 @@ react-is@^17.0.1:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
   integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
 
-react-refresh@^0.14.2:
+react-refresh@^0.14.0, react-refresh@^0.14.2:
   version "0.14.2"
   resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"
   integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==
@@ -3477,6 +6486,13 @@ react@19.0.0-rc-69d4b800-20241021:
   resolved "https://registry.yarnpkg.com/react/-/react-19.0.0-rc-69d4b800-20241021.tgz#2c3ce2a581df8c4c9e4059189af29b89022edd41"
   integrity sha512-dXki4tN+rP+4xhsm65q/QI/19VCZdu5vPcy4h6zaJt20XP8/1r/LCwrLFYuj8hElbNz5AmxW6JtRa7ej0BzZdg==
 
+"react@^16.8.0 || ^17.0.0 || ^18.0.0":
+  version "18.3.1"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
+  integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
+  dependencies:
+    loose-envify "^1.1.0"
+
 read-cache@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
@@ -3484,7 +6500,20 @@ read-cache@^1.0.0:
   dependencies:
     pify "^2.3.0"
 
-readable-stream@^3.6.0:
+readable-stream@^2.3.8:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
+  integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
+  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"
+
+readable-stream@^3.5.0, readable-stream@^3.6.0:
   version "3.6.2"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
   integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
@@ -3493,6 +6522,17 @@ readable-stream@^3.6.0:
     string_decoder "^1.1.1"
     util-deprecate "^1.0.1"
 
+readable-stream@^4.0.0:
+  version "4.5.2"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09"
+  integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==
+  dependencies:
+    abort-controller "^3.0.0"
+    buffer "^6.0.3"
+    events "^3.3.0"
+    process "^0.11.10"
+    string_decoder "^1.3.0"
+
 readdirp@~3.6.0:
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -3500,6 +6540,25 @@ readdirp@~3.6.0:
   dependencies:
     picomatch "^2.2.1"
 
+recast@^0.23.5:
+  version "0.23.9"
+  resolved "https://registry.yarnpkg.com/recast/-/recast-0.23.9.tgz#587c5d3a77c2cfcb0c18ccce6da4361528c2587b"
+  integrity sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==
+  dependencies:
+    ast-types "^0.16.1"
+    esprima "~4.0.0"
+    source-map "~0.6.1"
+    tiny-invariant "^1.3.3"
+    tslib "^2.0.1"
+
+redent@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
+  integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
+  dependencies:
+    indent-string "^4.0.0"
+    strip-indent "^3.0.0"
+
 reflect-metadata@^0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b"
@@ -3518,11 +6577,35 @@ reflect.getprototypeof@^1.0.4:
     globalthis "^1.0.3"
     which-builtin-type "^1.1.3"
 
+regenerate-unicode-properties@^10.2.0:
+  version "10.2.0"
+  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0"
+  integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==
+  dependencies:
+    regenerate "^1.4.2"
+
+regenerate@^1.4.2:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
+  integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
+
 regenerator-runtime@^0.14.0:
   version "0.14.1"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
   integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
 
+regenerator-transform@^0.15.2:
+  version "0.15.2"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4"
+  integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==
+  dependencies:
+    "@babel/runtime" "^7.8.4"
+
+regex-parser@^2.2.11:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.3.0.tgz#4bb61461b1a19b8b913f3960364bb57887f920ee"
+  integrity sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==
+
 regexp.prototype.flags@^1.5.2:
   version "1.5.3"
   resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42"
@@ -3533,6 +6616,51 @@ regexp.prototype.flags@^1.5.2:
     es-errors "^1.3.0"
     set-function-name "^2.0.2"
 
+regexpu-core@^6.1.1:
+  version "6.1.1"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.1.1.tgz#b469b245594cb2d088ceebc6369dceb8c00becac"
+  integrity sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==
+  dependencies:
+    regenerate "^1.4.2"
+    regenerate-unicode-properties "^10.2.0"
+    regjsgen "^0.8.0"
+    regjsparser "^0.11.0"
+    unicode-match-property-ecmascript "^2.0.0"
+    unicode-match-property-value-ecmascript "^2.1.0"
+
+regjsgen@^0.8.0:
+  version "0.8.0"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab"
+  integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==
+
+regjsparser@^0.11.0:
+  version "0.11.2"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.11.2.tgz#7404ad42be00226d72bcf1f003f1f441861913d8"
+  integrity sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==
+  dependencies:
+    jsesc "~3.0.2"
+
+relateurl@^0.2.7:
+  version "0.2.7"
+  resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
+  integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
+
+renderkid@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a"
+  integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==
+  dependencies:
+    css-select "^4.1.3"
+    dom-converter "^0.2.0"
+    htmlparser2 "^6.1.0"
+    lodash "^4.17.21"
+    strip-ansi "^6.0.1"
+
+require-from-string@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+  integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
 resolve-from@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@@ -3543,7 +6671,18 @@ resolve-pkg-maps@^1.0.0:
   resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f"
   integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
 
-resolve@^1.1.7, resolve@^1.22.2, resolve@^1.22.4:
+resolve-url-loader@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz#ee3142fb1f1e0d9db9524d539cfa166e9314f795"
+  integrity sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==
+  dependencies:
+    adjust-sourcemap-loader "^4.0.0"
+    convert-source-map "^1.7.0"
+    loader-utils "^2.0.0"
+    postcss "^8.2.14"
+    source-map "0.6.1"
+
+resolve@^1.1.7, resolve@^1.14.2, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4, resolve@^1.22.8:
   version "1.22.8"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
   integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
@@ -3573,6 +6712,14 @@ rimraf@^3.0.2:
   dependencies:
     glob "^7.1.3"
 
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
+  integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+
 rollup@^4.20.0:
   version "4.24.3"
   resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.24.3.tgz#8b259063740af60b0030315f88665ba2041789b8"
@@ -3622,11 +6769,16 @@ safe-array-concat@^1.1.2:
     has-symbols "^1.0.3"
     isarray "^2.0.5"
 
-safe-buffer@~5.2.0:
+safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
   version "5.2.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
 
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
 safe-regex-test@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377"
@@ -3641,6 +6793,13 @@ safe-regex-test@^1.0.3:
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
   integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
 
+sass-loader@^13.2.0:
+  version "13.3.3"
+  resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-13.3.3.tgz#60df5e858788cffb1a3215e5b92e9cba61e7e133"
+  integrity sha512-mt5YN2F1MOZr3d/wBRcZxeFgwgkH44wVc2zohO2YF6JiOMkiXe4BYRZpSu2sO1g71mo/j16txzUhsKZlqjVGzA==
+  dependencies:
+    neo-async "^2.6.2"
+
 saxes@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
@@ -3653,16 +6812,49 @@ scheduler@0.25.0-rc-69d4b800-20241021:
   resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-69d4b800-20241021.tgz#336e47ef2bd5eddb0ebacfd910b5df1b236d92bd"
   integrity sha512-S5AYX/YhMAN6u9AXgKYbZP4U4ZklC6R9Q7HmFSBk7d4DLiHVNxvAvlSvuM4nxFkwOk50MnpfTKQ7UWHXDOc9Eg==
 
+scheduler@^0.23.2:
+  version "0.23.2"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
+  integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
+  dependencies:
+    loose-envify "^1.1.0"
+
+schema-utils@^3.1.1, schema-utils@^3.2.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
+  integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
+  dependencies:
+    "@types/json-schema" "^7.0.8"
+    ajv "^6.12.5"
+    ajv-keywords "^3.5.2"
+
+schema-utils@^4.0.0, schema-utils@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b"
+  integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==
+  dependencies:
+    "@types/json-schema" "^7.0.9"
+    ajv "^8.9.0"
+    ajv-formats "^2.1.1"
+    ajv-keywords "^5.1.0"
+
 semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
   version "6.3.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
   integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
 
-semver@^7.3.5, semver@^7.6.0, semver@^7.6.3:
+semver@^7.3.5, semver@^7.3.7, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3:
   version "7.6.3"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
   integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
 
+serialize-javascript@^6.0.1:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
+  integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
+  dependencies:
+    randombytes "^2.1.0"
+
 server-only@^0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/server-only/-/server-only-0.0.1.tgz#0f366bb6afb618c37c9255a314535dc412cd1c9e"
@@ -3695,7 +6887,20 @@ set-function-name@^2.0.1, set-function-name@^2.0.2:
     functions-have-names "^1.2.3"
     has-property-descriptors "^1.0.2"
 
-sharp@^0.33.5:
+setimmediate@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+  integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
+
+sha.js@^2.4.0, sha.js@^2.4.8:
+  version "2.4.11"
+  resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
+  integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+sharp@^0.33.3, sharp@^0.33.5:
   version "0.33.5"
   resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e"
   integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==
@@ -3773,16 +6978,78 @@ source-map-js@^1.0.2, source-map-js@^1.2.1:
   resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
   integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
 
+source-map-support@~0.5.20:
+  version "0.5.21"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+  integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map@0.6.1, source-map@^0.6.0, source-map@~0.6.0, source-map@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@^0.7.3:
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
+  integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
+
 stackback@0.0.2:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
   integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
 
+stackframe@^1.3.4:
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
+  integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
+
 std-env@^3.7.0:
   version "3.7.0"
   resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2"
   integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==
 
+storybook-dark-mode@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/storybook-dark-mode/-/storybook-dark-mode-4.0.2.tgz#2536d1a229ac050172d37aa50bd9f6f7cdad0425"
+  integrity sha512-zjcwwQ01R5t1VsakA6alc2JDIRVtavryW8J3E3eKLDIlAMcvsgtpxlelWkZs2cuNspk6Z10XzhQVrUWtYc3F0w==
+  dependencies:
+    "@storybook/components" "^8.0.0"
+    "@storybook/core-events" "^8.0.0"
+    "@storybook/global" "^5.0.0"
+    "@storybook/icons" "^1.2.5"
+    "@storybook/manager-api" "^8.0.0"
+    "@storybook/theming" "^8.0.0"
+    fast-deep-equal "^3.1.3"
+    memoizerific "^1.11.3"
+
+storybook@^8.4.5:
+  version "8.4.5"
+  resolved "https://registry.yarnpkg.com/storybook/-/storybook-8.4.5.tgz#ba9589e2887958d1353dbc1a2cb6142e80e36a2c"
+  integrity sha512-9tfgabXnMibYp3SvoaJXXMD63Pw0SA9Hnf5v6TxysCYZs4DZ/04fAkK+9RW+K4C5JkV83qXMMlrsPj766R47fg==
+  dependencies:
+    "@storybook/core" "8.4.5"
+
+stream-browserify@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f"
+  integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==
+  dependencies:
+    inherits "~2.0.4"
+    readable-stream "^3.5.0"
+
+stream-http@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5"
+  integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==
+  dependencies:
+    builtin-status-codes "^3.0.0"
+    inherits "^2.0.4"
+    readable-stream "^3.6.0"
+    xtend "^4.0.2"
+
 streamsearch@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
@@ -3878,13 +7145,20 @@ string.prototype.trimstart@^1.0.8:
     define-properties "^1.2.1"
     es-object-atoms "^1.0.0"
 
-string_decoder@^1.1.1:
+string_decoder@^1.1.1, string_decoder@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
   integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
   dependencies:
     safe-buffer "~5.2.0"
 
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
 "strip-ansi-cjs@npm:strip-ansi@^6.0.1":
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
@@ -3899,7 +7173,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   dependencies:
     ansi-regex "^5.0.1"
 
-strip-ansi@^7.0.1:
+strip-ansi@^7.0.1, strip-ansi@^7.1.0:
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
   integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
@@ -3911,12 +7185,31 @@ strip-bom@^3.0.0:
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
   integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
 
+strip-indent@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
+  integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
+  dependencies:
+    min-indent "^1.0.0"
+
+strip-indent@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-4.0.0.tgz#b41379433dd06f5eae805e21d631e07ee670d853"
+  integrity sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==
+  dependencies:
+    min-indent "^1.0.1"
+
 strip-json-comments@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
   integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
 
-styled-jsx@5.1.6:
+style-loader@^3.3.1:
+  version "3.3.4"
+  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7"
+  integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==
+
+styled-jsx@5.1.6, styled-jsx@^5.1.6:
   version "5.1.6"
   resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499"
   integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==
@@ -3943,6 +7236,13 @@ supports-color@^7.1.0:
   dependencies:
     has-flag "^4.0.0"
 
+supports-color@^8.0.0:
+  version "8.1.1"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+  dependencies:
+    has-flag "^4.0.0"
+
 supports-preserve-symlinks-flag@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
@@ -3999,7 +7299,7 @@ tailwindcss@^3.4.1:
     resolve "^1.22.2"
     sucrase "^3.32.0"
 
-tapable@^2.2.0:
+tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
   integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
@@ -4016,6 +7316,27 @@ tar@^6.1.11:
     mkdirp "^1.0.3"
     yallist "^4.0.0"
 
+terser-webpack-plugin@^5.3.1, terser-webpack-plugin@^5.3.10:
+  version "5.3.10"
+  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199"
+  integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==
+  dependencies:
+    "@jridgewell/trace-mapping" "^0.3.20"
+    jest-worker "^27.4.5"
+    schema-utils "^3.1.1"
+    serialize-javascript "^6.0.1"
+    terser "^5.26.0"
+
+terser@^5.10.0, terser@^5.26.0:
+  version "5.36.0"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e"
+  integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==
+  dependencies:
+    "@jridgewell/source-map" "^0.3.3"
+    acorn "^8.8.2"
+    commander "^2.20.0"
+    source-map-support "~0.5.20"
+
 text-table@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -4035,6 +7356,18 @@ thenify-all@^1.0.0:
   dependencies:
     any-promise "^1.0.0"
 
+timers-browserify@^2.0.12:
+  version "2.0.12"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee"
+  integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==
+  dependencies:
+    setimmediate "^1.0.4"
+
+tiny-invariant@^1.3.1, tiny-invariant@^1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127"
+  integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==
+
 tinybench@^2.9.0:
   version "2.9.0"
   resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
@@ -4055,7 +7388,7 @@ tinyrainbow@^1.2.0:
   resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5"
   integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==
 
-tinyspy@^3.0.2:
+tinyspy@^3.0.0, tinyspy@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
   integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
@@ -4103,11 +7436,31 @@ ts-api-utils@^1.3.0:
   resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
   integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
 
+ts-dedent@^2.0.0, ts-dedent@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"
+  integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==
+
 ts-interface-checker@^0.1.9:
   version "0.1.13"
   resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
   integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
 
+ts-pnp@^1.1.6:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
+  integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
+
+tsconfig-paths-webpack-plugin@^4.0.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz#f7459a8ed1dd4cf66ad787aefc3d37fff3cf07fc"
+  integrity sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==
+  dependencies:
+    chalk "^4.1.0"
+    enhanced-resolve "^5.7.0"
+    tapable "^2.2.1"
+    tsconfig-paths "^4.1.2"
+
 tsconfig-paths@^3.15.0:
   version "3.15.0"
   resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
@@ -4118,7 +7471,16 @@ tsconfig-paths@^3.15.0:
     minimist "^1.2.6"
     strip-bom "^3.0.0"
 
-tslib@*, tslib@2, tslib@^2.4.0, tslib@^2.6.2:
+tsconfig-paths@^4.0.0, tsconfig-paths@^4.1.2, tsconfig-paths@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c"
+  integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==
+  dependencies:
+    json5 "^2.2.2"
+    minimist "^1.2.6"
+    strip-bom "^3.0.0"
+
+tslib@*, tslib@2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.4.0, tslib@^2.6.2:
   version "2.8.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
   integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
@@ -4135,6 +7497,16 @@ tsyringe@^4.8.0:
   dependencies:
     tslib "^1.9.3"
 
+tty-browserify@^0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
+  integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==
+
+tween-functions@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/tween-functions/-/tween-functions-1.2.0.tgz#1ae3a50e7c60bb3def774eac707acbca73bbc3ff"
+  integrity sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==
+
 type-check@^0.4.0, type-check@~0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -4147,6 +7519,11 @@ type-fest@^0.20.2:
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
   integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
 
+type-fest@^2.14.0, type-fest@^2.19.0:
+  version "2.19.0"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
+  integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
+
 typed-array-buffer@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3"
@@ -4206,11 +7583,34 @@ unbox-primitive@^1.0.2:
     has-symbols "^1.0.3"
     which-boxed-primitive "^1.0.2"
 
-undici-types@~6.19.2:
+undici-types@~6.19.2, undici-types@~6.19.8:
   version "6.19.8"
   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
   integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
 
+unicode-canonical-property-names-ecmascript@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2"
+  integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==
+
+unicode-match-property-ecmascript@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3"
+  integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==
+  dependencies:
+    unicode-canonical-property-names-ecmascript "^2.0.0"
+    unicode-property-aliases-ecmascript "^2.0.0"
+
+unicode-match-property-value-ecmascript@^2.1.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71"
+  integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==
+
+unicode-property-aliases-ecmascript@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd"
+  integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==
+
 universal-cookie@^7.0.0:
   version "7.2.2"
   resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.2.2.tgz#93ae9ec55baab89b24300473543170bb8112773c"
@@ -4219,6 +7619,19 @@ universal-cookie@^7.0.0:
     "@types/cookie" "^0.6.0"
     cookie "^0.7.2"
 
+universalify@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
+  integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+
+unplugin@^1.3.1:
+  version "1.16.0"
+  resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-1.16.0.tgz#ca0f248bf8798cd752dd02e5b381223b737cef72"
+  integrity sha512-5liCNPuJW8dqh3+DM6uNM2EI3MLLpCKp/KY+9pB5M2S2SR2qvvDHhKgBOaTWEbZTAws3CXfB0rKTIolWKL05VQ==
+  dependencies:
+    acorn "^8.14.0"
+    webpack-virtual-modules "^0.6.2"
+
 update-browserslist-db@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
@@ -4234,11 +7647,40 @@ uri-js@^4.2.2:
   dependencies:
     punycode "^2.1.0"
 
-util-deprecate@^1.0.1, util-deprecate@^1.0.2:
+url@^0.11.0:
+  version "0.11.4"
+  resolved "https://registry.yarnpkg.com/url/-/url-0.11.4.tgz#adca77b3562d56b72746e76b330b7f27b6721f3c"
+  integrity sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==
+  dependencies:
+    punycode "^1.4.1"
+    qs "^6.12.3"
+
+util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
   integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
 
+util@^0.12.4, util@^0.12.5:
+  version "0.12.5"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc"
+  integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
+  dependencies:
+    inherits "^2.0.3"
+    is-arguments "^1.0.4"
+    is-generator-function "^1.0.7"
+    is-typed-array "^1.1.3"
+    which-typed-array "^1.1.2"
+
+utila@~0.4:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
+  integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==
+
+uuid@^9.0.0:
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
+  integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
+
 vite-node@2.1.4:
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.4.tgz#97ffb6de913fd8d42253afe441f9512e9dbdfd5c"
@@ -4286,6 +7728,11 @@ vitest@^2.1.4:
     vite-node "2.1.4"
     why-is-node-running "^2.3.0"
 
+vm-browserify@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
+  integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
+
 void-elements@3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
@@ -4298,6 +7745,14 @@ w3c-xmlserializer@^5.0.0:
   dependencies:
     xml-name-validator "^5.0.0"
 
+watchpack@^2.4.1:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da"
+  integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==
+  dependencies:
+    glob-to-regexp "^0.4.1"
+    graceful-fs "^4.1.2"
+
 webidl-conversions@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@@ -4308,6 +7763,65 @@ webidl-conversions@^7.0.0:
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
   integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
 
+webpack-dev-middleware@^6.1.2:
+  version "6.1.3"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-6.1.3.tgz#79f4103f8c898564c9e96c3a9c2422de50f249bc"
+  integrity sha512-A4ChP0Qj8oGociTs6UdlRUGANIGrCDL3y+pmQMc+dSsraXHCatFpmMey4mYELA+juqwUqwQsUgJJISXl1KWmiw==
+  dependencies:
+    colorette "^2.0.10"
+    memfs "^3.4.12"
+    mime-types "^2.1.31"
+    range-parser "^1.2.1"
+    schema-utils "^4.0.0"
+
+webpack-hot-middleware@^2.25.1:
+  version "2.26.1"
+  resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.26.1.tgz#87214f1e3f9f3acab9271fef9e6ed7b637d719c0"
+  integrity sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==
+  dependencies:
+    ansi-html-community "0.0.8"
+    html-entities "^2.1.0"
+    strip-ansi "^6.0.0"
+
+webpack-sources@^3.2.3:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
+  integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
+
+webpack-virtual-modules@^0.6.0, webpack-virtual-modules@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8"
+  integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==
+
+webpack@5:
+  version "5.96.1"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.96.1.tgz#3676d1626d8312b6b10d0c18cc049fba7ac01f0c"
+  integrity sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==
+  dependencies:
+    "@types/eslint-scope" "^3.7.7"
+    "@types/estree" "^1.0.6"
+    "@webassemblyjs/ast" "^1.12.1"
+    "@webassemblyjs/wasm-edit" "^1.12.1"
+    "@webassemblyjs/wasm-parser" "^1.12.1"
+    acorn "^8.14.0"
+    browserslist "^4.24.0"
+    chrome-trace-event "^1.0.2"
+    enhanced-resolve "^5.17.1"
+    es-module-lexer "^1.2.1"
+    eslint-scope "5.1.1"
+    events "^3.2.0"
+    glob-to-regexp "^0.4.1"
+    graceful-fs "^4.2.11"
+    json-parse-even-better-errors "^2.3.1"
+    loader-runner "^4.2.0"
+    mime-types "^2.1.27"
+    neo-async "^2.6.2"
+    schema-utils "^3.2.0"
+    tapable "^2.1.1"
+    terser-webpack-plugin "^5.3.10"
+    watchpack "^2.4.1"
+    webpack-sources "^3.2.3"
+
 whatwg-encoding@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5"
@@ -4375,7 +7889,7 @@ which-collection@^1.0.2:
     is-weakmap "^2.0.2"
     is-weakset "^2.0.3"
 
-which-typed-array@^1.1.14, which-typed-array@^1.1.15:
+which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.2:
   version "1.1.15"
   resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d"
   integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==
@@ -4436,7 +7950,7 @@ wrappy@1:
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
-ws@^8.18.0:
+ws@^8.18.0, ws@^8.2.3:
   version "8.18.0"
   resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
   integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
@@ -4451,6 +7965,11 @@ xmlchars@^2.2.0:
   resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
   integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
 
+xtend@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
 yallist@^3.0.2:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
@@ -4461,6 +7980,11 @@ yallist@^4.0.0:
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
+yaml@^1.10.0:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+  integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
+
 yaml@^2.3.4:
   version "2.6.0"
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3"
@@ -4471,6 +7995,11 @@ yocto-queue@^0.1.0:
   resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
   integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
 
+yocto-queue@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110"
+  integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==
+
 zod@^3.23.8:
   version "3.23.8"
   resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
-- 
2.39.5