summary history files

commit:c59d49abf50f881b30a31d004a3bb062754b4fb3
date:Thu Apr 9 06:48:30 2026 +1000
parents:
feat: public git commit
diff --git a/.gitignore b/.gitignore
line changes: +6/-0
index 0000000..27aea40
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+/build
+/frontend/dist
+/kd
+/kt
+/website/html/release/

diff --git a/README.md b/README.md
line changes: +175/-0
index 0000000..7ae55eb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,175 @@
+# kd
+
+## Design
+
+### Kubernetes Deletion Options
+
+Use kubernetesDeleteOptions when deleting kubernetes resources.
+
+### Kubernetes Services in App
+
+All kubernetes service calls should occur in App.vue. This requires all
+child components to emit to App.
+
+This is intentional so that all kubernetes calls are in 1 file. In the
+future, I will migrate to tauri so limiting change to a few files when
+switching to rust is important.
+
+### Closing Modal
+
+Modals should be closed immediately, regardless of error. This will be
+the first iteration.
+
+The close should occur in the closet vue file, and not its parents.
+
+This is only for the first iteration of kd. I will improve on this
+later, but for now, closing modal immediately across all modals is good
+enough.
+
+### Creating Resources
+
+At least 2 modes should be supported when creating resources:
+1. Guided
+2. YAML
+
+With guided, kd will provide a form to create the resources with limited
+options. The options will default to values that "just work".
+
+With yaml, kd will provide the user with a free form text input box where
+the user can input yaml. JSON will be supported in the future so name of
+method creation will change from YAML to Raw.
+ 
+## Roadmap
+### v0.4
+
+Goals:
+ - Create a new Namespace, with a new Pod, attach a Secret and a PV
+   using Guided mode:
+     - To achieve this goal I will need to somehow be able to attach a
+       secret to a pod and also attach a PVC to a pod.
+ - Auto CI builds with forgejo runners
+
+- [ ] Pod:
+    - [X] Create guided Pod
+    - [X] Create raw Pod
+    - [X] Edit Pod
+    - [X] Update docs
+- [ ] Namespace:
+    - [X] List Namespace
+    - [X] Get Namespace
+    - [X] Create Namespace
+    - [X] Delete Namespace
+    - [X] Edit Namespace
+    - [ ] Update docs
+- [ ] Secret Support:
+    - [X] List Secret
+    - [X] Describe Secret
+    - [X] Delete Secret
+    - [X] Create Guided Secret
+    - [X] Edit Secret
+    - [ ] Update docs
+- [o] PV and PVC Support:
+    - [o] List PV
+    - [X] List PVC
+    - [X] Describe PV
+    - [X] Describe PVC
+    - [X] Create PVC
+    - [ ] Create PV
+    - [ ] Edit PV
+    - [ ] Edit PVC
+    - [ ] Update docs
+- [ ] Linux amd64 runner that builds binary
+- [ ] Windows amd64 runner that builds binary
+- [ ] Mac amd64 runner that builds binary
+- [ ] Mac arm64 runner that builds binary
+- Bugfixes:
+    - [ ] Logs autoscrolling
+    - [ ] Deployment Logs timing out
+
+
+
+### v0.3
+- [DONE] Multi cluster Support:
+    - Set multiple clusters in KUBECONFIG and display in navigation
+      panel.
+      - This includes having multiple clusters within a single file
+        aswell as multiple clusters within multiple files listed within
+        KUBECONFIG.
+- [DONE] Workload Statefulset support:
+    - List StatefulSets
+    - Describe StatefulSets
+    - Log StatefulSets
+- [DONE] ConfigMap Support:
+    - List ConfirMap
+    - Describe ConfigMap
+- [DONE] Network Ingress support:
+    - List ingresses
+    - Describe ingresses
+    - Delete Ingress
+- [DONE] Initial Search bar support:
+    - Search bar added to existing workloads
+    - Supports following queries:
+        - name:$name
+        - status:$status (for Pods only)
+        - namespace:$namespace
+        - label:$label
+- [DONE] Windows Platform support:
+    - Build and support Windows AMD64
+### Backlog
+- [ ] Deployment:
+    - [ ] Create Deployment
+    - [ ] Edit Deployment
+    - [ ] Delete Deployment
+- Services:
+    - Create a new service
+- Ingress:
+  - Add support for displaying Hosts, Address and Ports when listing Ingresses.
+- Use Patch instead of Delete/Create where possible:
+    - I believe I can patch resources instead of delete and create them
+      when user is to update the resource. Some (perhaps all) attributes
+      of a resource can be patched. Backend should attempt patch and if
+      that fails, fall back to delete & create method. This will result
+      in quicker update to the user.
+#### Labels arent supported
+Labels can be applied to resources. They can be searched on. They can be
+displayed in List view, edited or used when creating a resource.
+
+Resources are not supported in kd. Add support for them. At the very
+least:
+ * Labels should be displayed in List view
+ * Labels should be supported in Search
+For all supported resources
+
+[##](##) Bugs
+### deployment logs return error when client timeout for pods is reached
+
+When watching deployment logs which has Pods with multiple containers,
+the http client will timeout when trying to read error logs from other
+containers. When the context deadline is exceeded, the main container
+context is canceled so logs stop being displayed.
+
+To reproduce, watch logs for penny deployment. kd will attempt to
+connect to `queue` container aswell as `www` container. `queue`
+container will reach `context deadline exceeded` and will cancel the
+`www` stream.
+
+I can also reproduce this on `forgejo` deployment.
+
+### Logs not auto scrolling to bottom
+ - When user clicks on logs for current workloads, the logs pane should
+   scroll to bottom of logs and tail as new logs come in.
+
+### unable to resume terminal session
+When click on a Pod > Terminal, using the terminal then click somewhere
+else to get focus and click back on the terminal the second terminal
+session fails. Logs show "Unhandled Error websock close 100 User
+disconnected".
+### Support labels
+Kubernetes resources support labels. This is currently not supported by
+kd. Users will want to add, edit and delete labels from kubernetes
+resources.
+
+This work will need to be split into multiple cards, each representing
+label support for a given kubernetes resource. For example, "add label
+support for persistent volumes" will be one card and add "labels for
+secrets" will be another.

diff --git a/backend/cmd/kt/main.go b/backend/cmd/kt/main.go
line changes: +7/-0
index 0000000..3dfca99
--- /dev/null
+++ b/backend/cmd/kt/main.go
@@ -0,0 +1,7 @@
+package main
+
+import "kd/backend/internal/cli"
+
+func main() {
+	cli.Execute()
+}

diff --git a/backend/config/config.go b/backend/config/config.go
line changes: +173/-0
index 0000000..c184437
--- /dev/null
+++ b/backend/config/config.go
@@ -0,0 +1,173 @@
+package config
+
+import (
+	"fmt"
+	"runtime"
+	"strings"
+	"time"
+
+	"k8s.io/client-go/kubernetes"
+	restclient "k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
+	"k8s.io/client-go/tools/clientcmd/api"
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+)
+
+type Config struct {
+	KubeConfig         string
+	KubeConfigs        map[KubeConfigPath]*clientcmdapi.Config
+	DefaultKubeContext KubeContextName
+	KubeClusters       map[KubeContextName]KubeCluster
+	Opts
+}
+
+type KubeConfigPath string
+
+type KubeContextName string
+
+func (k KubeContextName) String() string {
+	return string(k)
+}
+
+type KubeCluster struct {
+	KubeContext    *api.Context
+	KubeCluster    *api.Cluster
+	KubeClientSet  *kubernetes.Clientset
+	KubeRestConfig *restclient.Config
+}
+
+type Opts struct {
+	Debug bool
+}
+
+type OptFunc func(*Opts)
+
+func defaultOpts() Opts {
+	return Opts{
+		Debug: false,
+	}
+}
+
+func WithDebug(d bool) OptFunc {
+	return func(opts *Opts) {
+		opts.Debug = d
+	}
+}
+
+func (c Config) GetContext(name string) (*api.Context, error) {
+	kubeContext, ok := c.KubeClusters[KubeContextName(name)]
+	if !ok {
+		return nil, fmt.Errorf("context does not exist")
+	}
+	return kubeContext.KubeContext, nil
+}
+
+func (c Config) GetClientSet(contextName string) (*kubernetes.Clientset, error) {
+	kubeContext, ok := c.KubeClusters[KubeContextName(contextName)]
+	if !ok {
+		return &kubernetes.Clientset{}, fmt.Errorf("context does not exist")
+	}
+	return kubeContext.KubeClientSet, nil
+}
+
+func (c Config) GetRestConfig(contextName string) (*restclient.Config, error) {
+	kubeContext, ok := c.KubeClusters[KubeContextName(contextName)]
+	if !ok {
+		return &restclient.Config{}, fmt.Errorf("context does not exist")
+	}
+	return kubeContext.KubeRestConfig, nil
+}
+
+func (c Config) getKubeContext(s string) (*api.Context, bool) {
+	for _, apiConfig := range c.KubeConfigs {
+		for name, context := range apiConfig.Contexts {
+			if strings.ToLower(s) == name {
+				return context, true
+			}
+		}
+	}
+	return nil, false
+}
+
+func (c Config) getKubeCluster(s string) (*api.Cluster, bool) {
+	for _, apiConfig := range c.KubeConfigs {
+		for name, cluster := range apiConfig.Clusters {
+			if strings.ToLower(s) == name {
+				return cluster, true
+			}
+		}
+	}
+	return nil, false
+}
+
+func NewConfig(kubeConfig string, opts ...OptFunc) (Config, error) {
+	c := Config{
+		KubeClusters: map[KubeContextName]KubeCluster{},
+	}
+
+	o := defaultOpts()
+	for _, fn := range opts {
+		fn(&o)
+	}
+	c.Opts = o
+
+	kubeConfigs := map[KubeConfigPath]*clientcmdapi.Config{}
+
+	// NOTE(rene): on windows path may have : so only support single kubeconfig
+	kConfig := make([]string, 0)
+	switch runtime.GOOS {
+	case "windows":
+		kConfig = append(kConfig, kubeConfig)
+	default:
+		kConfig = strings.Split(kubeConfig, ":")
+	}
+
+	for _, i := range kConfig {
+		apiConfig, err := clientcmd.LoadFromFile(i)
+		if err != nil {
+			return c, err
+		}
+		kubeConfigs[KubeConfigPath(i)] = apiConfig
+	}
+	c.KubeConfigs = kubeConfigs
+
+	for _, apiConfig := range c.KubeConfigs {
+		for name, context := range apiConfig.Contexts {
+			cluster, ok := c.getKubeCluster(name)
+			if !ok {
+				continue
+			}
+
+			configOverrides := clientcmd.ConfigOverrides{
+				Context: api.Context{
+					Cluster:   context.Cluster,
+					Namespace: context.Namespace,
+					AuthInfo:  context.AuthInfo,
+				},
+			}
+
+			restConfig, err := clientcmd.NewNonInteractiveClientConfig(*apiConfig, name, &configOverrides, nil).ClientConfig()
+			if err != nil {
+				return c, err
+			}
+
+			restConfig.Timeout = 30 * time.Second
+
+			clientSet, err := kubernetes.NewForConfig(restConfig)
+			if err != nil {
+				return c, err
+			}
+
+			m := KubeCluster{
+				KubeContext:    context,
+				KubeCluster:    cluster,
+				KubeRestConfig: restConfig,
+				KubeClientSet:  clientSet,
+			}
+
+			c.KubeClusters[KubeContextName(name)] = m
+		}
+	}
+
+	return c, nil
+}

diff --git a/backend/internal/cli/cli.go b/backend/internal/cli/cli.go
line changes: +66/-0
index 0000000..61a3a82
--- /dev/null
+++ b/backend/internal/cli/cli.go
@@ -0,0 +1,66 @@
+package cli
+
+import (
+	"context"
+	"kd/backend/config"
+	"kd/backend/logwrap"
+	"os"
+	"path"
+
+	"github.com/spf13/cobra"
+)
+
+type cli struct {
+	debug  bool
+	conf   config.Config
+	logger *logwrap.LogWrap
+}
+
+func (c *cli) init() error {
+	kubeConfigPath, ok := os.LookupEnv("KUBECONFIG")
+	if !ok {
+		homeDir, err := os.UserHomeDir()
+		if err != nil {
+			return err
+		}
+		kubeConfigPath = path.Join(homeDir, ".kube", "config.yaml")
+	}
+
+	configOpts := []config.OptFunc{
+		config.WithDebug(c.debug),
+	}
+
+	conf, err := config.NewConfig(kubeConfigPath, configOpts...)
+	if err != nil {
+		return err
+	}
+	c.conf = conf
+
+	logger := logwrap.New("logger", os.Stdout, true)
+	logger.SetLevel(logwrap.INFO)
+	if c.debug {
+		logger.SetLevel(logwrap.DEBUG)
+	}
+	c.logger = logger
+
+	return nil
+}
+
+func Execute() {
+	cli := &cli{}
+	rootCmd := buildRootCmd(cli)
+	if err := rootCmd.ExecuteContext(context.TODO()); err != nil {
+		os.Exit(1)
+	}
+}
+
+func buildRootCmd(cli *cli) *cobra.Command {
+	rootCmd := &cobra.Command{
+		Use: "kt",
+		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+			return cli.init()
+		},
+	}
+	rootCmd.AddCommand(clusterCmd(cli))
+	return rootCmd
+}

diff --git a/backend/internal/cli/cluster.go b/backend/internal/cli/cluster.go
line changes: +81/-0
index 0000000..ae4d571
--- /dev/null
+++ b/backend/internal/cli/cluster.go
@@ -0,0 +1,81 @@
+package cli
+
+import (
+	"encoding/json"
+	"fmt"
+	"kd/backend/services"
+
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+)
+
+func clusterCmd(cli *cli) *cobra.Command {
+	var cmd = &cobra.Command{
+		Use: "cluster",
+	}
+	cmd.AddCommand(clusterGetDeploymentsCmd(cli))
+	cmd.AddCommand(clusterGetPodsCmd(cli))
+	return cmd
+}
+
+func clusterGetDeploymentsCmd(cli *cli) *cobra.Command {
+	var flags struct {
+		contextName string
+		namespace   string
+	}
+
+	var cmd = &cobra.Command{
+		Use: "get-deployments",
+		PreRun: func(cmd *cobra.Command, args []string) {
+			_ = viper.BindPFlag("context-name", cmd.Flags().Lookup("context-name"))
+			_ = viper.BindPFlag("namespace", cmd.Flags().Lookup("namespace"))
+		},
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clusterSvc := services.Cluster()
+			clusterSvc.Start(cmd.Context(), cli.conf, cli.logger)
+			resp := clusterSvc.GetDeployments(flags.contextName, flags.namespace)
+			fmt.Printf("resp: %#+v\n", resp)
+			return nil
+		},
+	}
+
+	cmd.Flags().StringVar(&flags.contextName, "context-name", "", "context-name")
+	cmd.Flags().StringVar(&flags.namespace, "namespace", "", "namespace")
+	cmd.MarkFlagRequired("context-name")
+
+	return cmd
+}
+
+func clusterGetPodsCmd(cli *cli) *cobra.Command {
+	var flags struct {
+		contextName string
+		namespace   string
+	}
+
+	var cmd = &cobra.Command{
+		Use: "get-pods",
+		PreRun: func(cmd *cobra.Command, args []string) {
+			_ = viper.BindPFlag("context-name", cmd.Flags().Lookup("context-name"))
+			_ = viper.BindPFlag("namespace", cmd.Flags().Lookup("namespace"))
+		},
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clusterSvc := services.Cluster()
+			clusterSvc.Start(cmd.Context(), cli.conf, cli.logger)
+			resp := clusterSvc.GetPods(flags.contextName, flags.namespace)
+			for _, pod := range resp.Data {
+				b, err := json.MarshalIndent(pod, "", "    ")
+				if err != nil {
+					return err
+				}
+				fmt.Println(string(b))
+			}
+			return nil
+		},
+	}
+
+	cmd.Flags().StringVar(&flags.contextName, "context-name", "", "context-name")
+	cmd.Flags().StringVar(&flags.namespace, "namespace", "", "namespace")
+	cmd.MarkFlagRequired("context-name")
+
+	return cmd
+}

diff --git a/backend/logwrap/logwrap.go b/backend/logwrap/logwrap.go
line changes: +123/-0
index 0000000..8d654d1
--- /dev/null
+++ b/backend/logwrap/logwrap.go
@@ -0,0 +1,123 @@
+package logwrap
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"path/filepath"
+	"runtime"
+	"sync"
+)
+
+const (
+	DEBUG   = 0
+	INFO    = 10
+	WARNING = 20
+	ERROR   = 30
+	NONE    = 100
+)
+
+var (
+	pkgLock sync.Mutex
+	allLogs = make(map[string]*LogWrap)
+)
+
+type LogWrap struct {
+	name         string
+	level        int
+	errorLogWrap *log.Logger
+	debugLogWrap *log.Logger
+	infoLogWrap  *log.Logger
+	logfileinfo  bool
+	lock         sync.Mutex
+}
+
+func New(name string, dest io.Writer, logfileinfo bool, flags ...int) *LogWrap {
+	pkgLock.Lock()
+	defer pkgLock.Unlock()
+	if _, exists := allLogs[name]; exists {
+		panic(fmt.Sprintf("Unable to create logger with %s: name already in use", name))
+	}
+
+	var logFlags int
+	if flags == nil {
+		logFlags = log.Ldate | log.Ltime | log.Lmsgprefix
+	} else {
+		logFlags = flags[0]
+	}
+
+	logger := &LogWrap{
+		name:         name,
+		logfileinfo:  logfileinfo,
+		errorLogWrap: log.New(dest, "ERROR: ", logFlags),
+		debugLogWrap: log.New(dest, "DEBUG: ", logFlags),
+		infoLogWrap:  log.New(dest, "INFO: ", logFlags),
+	}
+
+	allLogs[name] = logger
+	return logger
+}
+
+func getfileinfo() string {
+	_, filename, line, ok := runtime.Caller(2)
+	if !ok {
+		filename = "Unknown"
+		line = 0
+	}
+	return fmt.Sprintf("%s:%d: ", filepath.Base(filename), line)
+}
+
+func (l *LogWrap) SetLevel(level int) {
+	l.lock.Lock()
+	defer l.lock.Unlock()
+	l.level = level
+}
+
+func (l *LogWrap) GetLevel() int {
+	return l.level
+}
+
+func (l *LogWrap) Info(msg string) {
+	l.lock.Lock()
+	defer l.lock.Unlock()
+	if l.level > INFO {
+		return
+	}
+	if l.logfileinfo {
+		msg = getfileinfo() + msg
+	}
+	l.infoLogWrap.Println(msg)
+}
+
+func (l *LogWrap) Error(msg string) {
+	l.lock.Lock()
+	defer l.lock.Unlock()
+	if l.level > ERROR {
+		return
+	}
+	if l.logfileinfo {
+		msg = getfileinfo() + msg
+	}
+	l.errorLogWrap.Println(msg)
+}
+
+func (l *LogWrap) Debug(msg string) {
+	l.lock.Lock()
+	defer l.lock.Unlock()
+	if l.level > DEBUG {
+		return
+	}
+	if l.logfileinfo {
+		msg = getfileinfo() + msg
+	}
+	l.debugLogWrap.Println(msg)
+}
+
+func Get(name string) *LogWrap {
+	pkgLock.Lock()
+	defer pkgLock.Unlock()
+	if foundLog, ok := allLogs[name]; ok {
+		return foundLog
+	}
+	return nil
+}

diff --git a/backend/services/cluster_service.go b/backend/services/cluster_service.go
line changes: +998/-0
index 0000000..3d1d336
--- /dev/null
+++ b/backend/services/cluster_service.go
@@ -0,0 +1,998 @@
+package services
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"kd/backend/config"
+	"kd/backend/logwrap"
+	"kd/backend/types"
+	"strings"
+	"sync"
+	"time"
+
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	k8types "k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/wait"
+	"k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/util/retry"
+	"k8s.io/kubectl/pkg/describe"
+	describecmd "k8s.io/kubectl/pkg/describe"
+	"k8s.io/kubectl/pkg/scheme"
+)
+
+type clusterService struct {
+	ctx    context.Context
+	conf   config.Config
+	logger *logwrap.LogWrap
+}
+
+var cluster *clusterService
+var onceCluster sync.Once
+
+func Cluster() *clusterService {
+	if cluster == nil {
+		onceCluster.Do(func() {
+			cluster = &clusterService{}
+		})
+	}
+	return cluster
+}
+
+func (c *clusterService) Start(ctx context.Context, conf config.Config, logger *logwrap.LogWrap) {
+	c.ctx = ctx
+	c.conf = conf
+	c.logger = logger
+}
+
+func (c *clusterService) GetClusters() types.ClustersResponse {
+	r := types.NewClustersResponse()
+	for k := range c.conf.KubeClusters {
+		cluster := types.Cluster{ContextName: k.String()}
+		r.Data = append(r.Data, cluster)
+	}
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) GetNamespace(contextName, name string) types.NamespaceResponse {
+	r := types.NewNamespaceResponse()
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	namespace, err := cs.CoreV1().Namespaces().Get(c.ctx, name, metav1.GetOptions{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to get Namespace: %s", err.Error())
+		return r
+	}
+	r.Data = *namespace
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) GetNamespaces(context string) types.NamespacesResponse {
+	r := types.NewNamespacesResponse()
+
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	namespacesClient := cs.CoreV1().Namespaces()
+	namespacesList, err := namespacesClient.List(c.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = "Failed to list Namespaces"
+		return r
+	}
+
+	for _, namespace := range namespacesList.Items {
+		r.Data = append(r.Data, namespace)
+	}
+
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) RestartDeployment(contextName, namespaceName, name string) types.DeploymentResponse {
+	r := types.NewDeploymentResponse()
+
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	deploymentsClient := cs.AppsV1().Deployments(namespaceName)
+	deployment, err := deploymentsClient.Get(c.ctx, name, metav1.GetOptions{})
+	if err != nil {
+		r.Msg = "Failed to get Deployment"
+		return r
+	}
+
+	deployment.Spec.Template.Annotations = make(map[string]string)
+	deployment.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339)
+	deployment, err = deploymentsClient.Update(c.ctx, deployment, metav1.UpdateOptions{})
+	if err != nil {
+		r.Msg = "Failed to update Deployment"
+		return r
+	}
+	r.Data = *deployment
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) RestartStatefulSet(contextName, namespaceName, name string) types.StatefulSetResponse {
+	r := types.NewStatefulSetResponse()
+
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	statefulSetsClient := cs.AppsV1().StatefulSets(namespaceName)
+	statefulSet, err := statefulSetsClient.Get(c.ctx, name, metav1.GetOptions{})
+	if err != nil {
+		r.Msg = "Failed to get StatefulSet"
+		return r
+	}
+
+	statefulSet.Spec.Template.Annotations = make(map[string]string)
+	statefulSet.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339)
+	statefulSet, err = statefulSetsClient.Update(c.ctx, statefulSet, metav1.UpdateOptions{})
+	if err != nil {
+		r.Msg = "Failed to update StatefulSet"
+		return r
+	}
+	r.Data = *statefulSet
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) GetStatefulSets(contextName string, namespace string) types.StatefulSetsResponse {
+	r := types.NewStatefulSetsResponse()
+
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find context name %s", contextName)
+		return r
+	}
+
+	statefulSetsClient := cs.AppsV1().StatefulSets(namespace)
+	statefulSetsList, err := statefulSetsClient.List(c.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to list StatefulSets for %s: %v", contextName, err)
+		return r
+	}
+
+	for _, statefulSet := range statefulSetsList.Items {
+		r.Data = append(r.Data, statefulSet)
+	}
+
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) GetDeployments(context string, namespaceName string) types.DeploymentsResponse {
+	r := types.NewDeploymentsResponse()
+
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find context name %s", context)
+		return r
+	}
+
+	deploymentsClient := cs.AppsV1().Deployments(namespaceName)
+	deploymentsList, err := deploymentsClient.List(c.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to list Deployments for %s: %v", context, err)
+		return r
+	}
+
+	for _, deployment := range deploymentsList.Items {
+		r.Data = append(r.Data, deployment)
+	}
+
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) GetPod(contextName, namespaceName, name string) types.PodResponse {
+	r := types.NewPodResponse()
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	podsClient := cs.CoreV1().Pods(namespaceName)
+	pod, err := podsClient.Get(c.ctx, name, metav1.GetOptions{})
+	if err != nil {
+		r.Msg = "Failed to list Pods"
+		return r
+	}
+	r.Data = *pod
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) GetPods(contextName, namespaceName string) types.PodsResponse {
+	r := types.NewPodsResponse()
+
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	podsClient := cs.CoreV1().Pods(namespaceName)
+	podsList, err := podsClient.List(c.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = "Failed to list Pods"
+		return r
+	}
+	r.Data = podsList.Items
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) GetConfigMaps(contextName, namespaceName string) types.ConfigMapsResponse {
+	r := types.NewConfigMapsResponse()
+
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	configMapsClient := cs.CoreV1().ConfigMaps(namespaceName)
+	configMapsList, err := configMapsClient.List(c.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = "Failed to list ConfigMaps"
+		return r
+	}
+	r.Data = configMapsList.Items
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) GetIngresses(context, namespace string) types.IngressesResponse {
+	r := types.NewIngressesResponse()
+
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	client := cs.NetworkingV1().Ingresses(namespace)
+	out, err := client.List(c.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = "Failed to list ConfigMaps"
+		return r
+	}
+	r.Data = out.Items
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DescribeConfigMap(context, namespace, name string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := c.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find rest config name: %s", err.Error())
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "", Kind: "ConfigMap"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = "Failed to find describer"
+		return r
+	}
+
+	out, err := d.Describe(namespace, name, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to describe resource: %s", err.Error())
+		return r
+	}
+	r.Data = out
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DescribePod(context, namespace, name string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := c.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = "Failed to find rest config name"
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "", Kind: "Pod"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = "Failed to find describer"
+		return r
+	}
+
+	out, err := d.Describe(namespace, name, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = "Failed to describe pod"
+		return r
+	}
+	r.Data = out
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DescribeDeployment(context, namespace, name string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := c.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = "Failed to find rest config name"
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "apps", Kind: "Deployment"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = "Failed to find describer"
+		return r
+	}
+
+	out, err := d.Describe(namespace, name, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = "Failed to describe deployment"
+		return r
+	}
+	r.Data = out
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DescribeStatefulSet(context, namespace, name string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := c.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = "Failed to find rest config name"
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "apps", Kind: "StatefulSet"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = "Failed to find describer"
+		return r
+	}
+
+	out, err := d.Describe(namespace, name, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = "Failed to describe statefulset"
+		return r
+	}
+	r.Data = out
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DescribeNamespace(context, namespace string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := c.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = "Failed to find rest config name"
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "", Kind: "Namespace"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = "Failed to find describer"
+		return r
+	}
+
+	out, err := d.Describe(namespace, namespace, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = "Failed to describe namespace"
+		return r
+	}
+	r.Data = out
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DescribeIngress(context, namespace, name string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := c.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = "Failed to find rest config name"
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "networking.k8s.io", Kind: "Ingress"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = "Failed to find describer"
+		return r
+	}
+
+	out, err := d.Describe(namespace, name, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = "Failed to describe ingress"
+		return r
+	}
+	r.Data = out
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DeletePod(contextName, namespaceName, podName string) types.Response {
+	r := types.NewResponse()
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	podsClient := cs.CoreV1().Pods(namespaceName)
+
+	deletePolicy := metav1.DeletePropagationForeground
+	if err := podsClient.Delete(c.ctx, podName, metav1.DeleteOptions{PropagationPolicy: &deletePolicy}); err != nil {
+		r.Msg = "Failed to delete Pod"
+		return r
+	}
+
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DeleteIngress(context, namespace, name string) types.Response {
+	r := types.NewResponse()
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	client := cs.NetworkingV1().Ingresses(namespace)
+	deletePolicy := metav1.DeletePropagationForeground
+	if err := client.Delete(c.ctx, name, metav1.DeleteOptions{PropagationPolicy: &deletePolicy}); err != nil {
+		r.Msg = "Failed to delete Ingress"
+		return r
+	}
+
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) getNamespaceFromCustomNamespace(customNamespace any) (*corev1.Namespace, error) {
+	namespace := &corev1.Namespace{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "v1",
+			Kind:       "Namespace",
+		},
+		ObjectMeta: metav1.ObjectMeta{},
+		Spec:       corev1.NamespaceSpec{},
+	}
+
+	namespaceMap, ok := customNamespace.(map[string]interface{})
+	if !ok {
+		return namespace, fmt.Errorf("unable to convert custom namespace to runtime object")
+	}
+
+	if metadata, ok := namespaceMap["metadata"].(map[string]interface{}); ok {
+		name, ok := metadata["name"].(string)
+		if ok {
+			namespace.ObjectMeta.Name = name
+		}
+	}
+	return namespace, nil
+}
+
+// getPodFromCustomPod accepts any and returns a corev1.Pod. customPod is sent
+// from frontend and as of 2025-04-28 is a map[string]interface. Normalising of
+// the customPod needs to occur so that it can be coerced into a corev1.Pod,
+// such as the pod.Spec.Container[0].Command.
+//
+// As of 2025-04-28 this only supports guided customPods.
+func (c *clusterService) getPodFromCustomPod(customPod any) (*corev1.Pod, error) {
+	pod := &corev1.Pod{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "v1",
+			Kind:       "Pod",
+		},
+		ObjectMeta: metav1.ObjectMeta{},
+		Spec:       corev1.PodSpec{},
+	}
+
+	// Convert podDefinition to a runtime.Object
+	podMap, ok := customPod.(map[string]interface{})
+	if !ok {
+		return pod, fmt.Errorf("unable to convert custom pod to runtime object")
+	}
+
+	// Extract metadata
+	if metadataMap, ok := podMap["metadata"].(map[string]interface{}); ok {
+		if name, ok := metadataMap["name"].(string); ok {
+			pod.Name = name
+		}
+		if namespace, ok := metadataMap["namespace"].(string); ok {
+			pod.Namespace = namespace
+		}
+	}
+
+	// Extract containers
+	if specMap, ok := podMap["spec"].(map[string]interface{}); ok {
+		if containersSlice, ok := specMap["containers"].([]interface{}); ok {
+			for _, containerInterface := range containersSlice {
+				if containerMap, ok := containerInterface.(map[string]interface{}); ok {
+					container := corev1.Container{}
+
+					for key, value := range containerMap {
+						switch key {
+						case "name":
+							if name, ok := value.(string); ok {
+								container.Name = name
+							}
+						case "image":
+							if image, ok := value.(string); ok {
+								container.Image = image
+							}
+						case "command":
+							switch cmdValue := value.(type) {
+							case []interface{}:
+								command := make([]string, 0, len(cmdValue))
+								for _, cmdItem := range cmdValue {
+									if cmdStr, ok := cmdItem.(string); ok {
+										command = append(command, cmdStr)
+									}
+								}
+
+								// Special handling for "sh -c" commands
+								if len(command) >= 3 && command[0] == "sh" && command[1] == "-c" {
+									// Combine all arguments after "sh -c" into a single string
+									shellCommand := strings.Join(command[2:], " ")
+									command = []string{"sh", "-c", shellCommand}
+								}
+								container.Command = command
+							case string:
+								var commandSlice []string
+
+								// Try to unmarshal as JSON array
+								if err := json.Unmarshal([]byte(cmdValue), &commandSlice); err == nil {
+									// Special handling for "sh -c" commands
+									if len(commandSlice) >= 3 && commandSlice[0] == "sh" && commandSlice[1] == "-c" {
+										// Combine all arguments after "sh -c" into a single string
+										shellCommand := strings.Join(commandSlice[2:], " ")
+										commandSlice = []string{"sh", "-c", shellCommand}
+									}
+
+									container.Command = commandSlice
+								} else {
+									// If JSON parsing fails, manually parse the string
+									cmdStr := strings.TrimSpace(cmdValue)
+
+									// Check if it looks like a JSON array
+									if strings.HasPrefix(cmdStr, "[") && strings.HasSuffix(cmdStr, "]") {
+										// Remove the brackets
+										cmdStr = cmdStr[1 : len(cmdStr)-1]
+
+										// Split by commas and process each part
+										parts := strings.Split(cmdStr, ",")
+										command := make([]string, 0, len(parts))
+
+										for _, part := range parts {
+											// Trim spaces and quotes
+											part = strings.TrimSpace(part)
+											if strings.HasPrefix(part, "\"") && strings.HasSuffix(part, "\"") {
+												part = part[1 : len(part)-1]
+											}
+											if part != "" {
+												command = append(command, part)
+											}
+										}
+
+										// Special handling for "sh -c" commands
+										if len(command) >= 3 && command[0] == "sh" && command[1] == "-c" {
+											// Combine all arguments after "sh -c" into a single string
+											shellCommand := strings.Join(command[2:], " ")
+											command = []string{"sh", "-c", shellCommand}
+										}
+
+										container.Command = command
+									} else {
+										// If it doesn't look like a JSON array, split by spaces
+										parts := strings.Fields(cmdStr)
+
+										// Special handling for "sh -c" commands
+										if len(parts) >= 3 && parts[0] == "sh" && parts[1] == "-c" {
+											// Combine all arguments after "sh -c" into a single string
+											shellCommand := strings.Join(parts[2:], " ")
+											parts = []string{"sh", "-c", shellCommand}
+										}
+
+										container.Command = parts
+									}
+								}
+							}
+						}
+					}
+					pod.Spec.Containers = append(pod.Spec.Containers, container)
+				}
+			}
+		}
+	}
+
+	// Ensure namespace is set
+	if pod.Namespace == "" {
+		pod.Namespace = "default"
+	}
+
+	return pod, nil
+}
+
+func (c *clusterService) CreateNamespace(context string, definition any) types.Response {
+	r := types.NewResponse()
+
+	namespace, err := c.getNamespaceFromCustomNamespace(definition)
+	if err != nil {
+		r.Msg = "Failed to create namespace"
+		c.logger.Error(fmt.Sprintf("%s: %s", r.Msg, err.Error()))
+		return r
+	}
+
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find context name: %s", err.Error())
+		return r
+	}
+
+	client := cs.CoreV1().Namespaces()
+	if _, err := client.Create(c.ctx, namespace, metav1.CreateOptions{}); err != nil {
+		r.Msg = "Failed to create namespace"
+		c.logger.Error(fmt.Sprintf("%s: %s", r.Msg, err.Error()))
+		return r
+	}
+
+	r.Msg = fmt.Sprintf("Namespace created: %s", namespace.GetName())
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) CreatePod(context string, podDefinition any) types.Response {
+	r := types.NewResponse()
+
+	pod, err := c.getPodFromCustomPod(podDefinition)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to create Pod: %s", err)
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	client := cs.CoreV1().Pods(pod.GetNamespace())
+	if _, err := client.Create(c.ctx, pod, metav1.CreateOptions{}); err != nil {
+		r.Msg = fmt.Sprintf("Failed to create Pod: %s", err)
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	r.Msg = fmt.Sprintf("Pod created: %s", pod.GetName())
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) CreateNamespaceYAML(context string, s string) types.Response {
+	r := types.NewResponse()
+
+	namespace, err := c.createNamespaceFromYamlWithCodec(s)
+	if err != nil {
+		r.Msg = "Failed to create Namespace from YAML"
+		c.logger.Error(fmt.Sprintf("%s: %s", r.Msg, err))
+		return r
+	}
+
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	client := cs.CoreV1().Namespaces()
+	if _, err := client.Create(c.ctx, namespace, metav1.CreateOptions{}); err != nil {
+		r.Msg = fmt.Sprintf("Failed to create Namespace: %s", err)
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	r.Msg = fmt.Sprintf("Namespace created: %s", namespace.GetName())
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) CreatePodYAML(context string, s string) types.Response {
+	r := types.NewResponse()
+
+	pod, err := c.createPodFromYamlWithCodec(s)
+	if err != nil {
+		r.Msg = "Failed to create Pod from YAML"
+		c.logger.Error(fmt.Sprintf("%s: %s", r.Msg, err))
+		return r
+	}
+
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	client := cs.CoreV1().Pods(pod.GetNamespace())
+	if _, err := client.Create(c.ctx, pod, metav1.CreateOptions{}); err != nil {
+		r.Msg = fmt.Sprintf("Failed to create Pod: %s", err)
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	r.Msg = fmt.Sprintf("Pod created: %s", pod.GetName())
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) DeleteNamespace(context, name string) types.Response {
+	r := types.NewResponse()
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	namespaceClient := cs.CoreV1().Namespaces()
+	deletePolicy := metav1.DeletePropagationForeground
+	if err := namespaceClient.Delete(c.ctx, name, metav1.DeleteOptions{PropagationPolicy: &deletePolicy}); err != nil {
+		r.Msg = "Failed to delete Namespace"
+		return r
+	}
+
+	r.Success = true
+	return r
+}
+
+func (c *clusterService) createNamespaceFromYamlWithCodec(yamlContent string) (*corev1.Namespace, error) {
+	decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
+	obj, gvk, err := decode([]byte(yamlContent), nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if gvk.Kind != "Namespace" {
+		return nil, fmt.Errorf("YAML does not define a Namespace resource, got: %s", gvk.Kind)
+	}
+
+	namespace, ok := obj.(*corev1.Namespace)
+	if !ok {
+		return nil, fmt.Errorf("failed to convert object to Namespace")
+	}
+
+	return namespace, nil
+}
+
+func (c *clusterService) createPodFromYamlWithCodec(yamlContent string) (*corev1.Pod, error) {
+	decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
+	obj, gvk, err := decode([]byte(yamlContent), nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check if the object is a Pod
+	if gvk.Kind != "Pod" {
+		return nil, fmt.Errorf("YAML does not define a Pod resource, got: %s", gvk.Kind)
+	}
+
+	pod, ok := obj.(*corev1.Pod)
+	if !ok {
+		return nil, fmt.Errorf("failed to convert object to Pod")
+	}
+
+	return pod, nil
+}
+
+// patchNamespace takes a namespace name and a slice of bytes which represents
+// a patch to the namespace resource. It will attempt to patch the namespace.
+func (c *clusterService) patchNamespace(clientset *kubernetes.Clientset, name string, patchData []byte) error {
+	return retry.RetryOnConflict(retry.DefaultRetry, func() error {
+		_, err := clientset.CoreV1().Namespaces().Patch(
+			context.TODO(),
+			name,
+			k8types.StrategicMergePatchType, // You can also use types.MergePatchType or types.JSONPatchType
+			patchData,
+			metav1.PatchOptions{},
+		)
+		return err
+	})
+}
+
+func (c *clusterService) UpdateNamespaceFromYaml(context, yamlContent, originalName string) types.NamespaceResponse {
+	r := types.NewNamespaceResponse()
+
+	cs, err := c.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
+	obj, gvk, err := decode([]byte(yamlContent), nil, nil)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to parse YAML: %v", err)
+		return r
+	}
+
+	if gvk.Kind != "Namespace" {
+		r.Msg = "YAML does not define a Namespace resource"
+		return r
+	}
+
+	b, err := json.Marshal(obj)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to convert object to JSON: %v", err)
+		return r
+	}
+
+	if err := c.patchNamespace(cs, originalName, b); err != nil {
+		r.Msg = fmt.Sprintf("Failed to patch Namespace: %v", err)
+		return r
+	}
+
+	r.Msg = "Namespace updated"
+	r.Success = true
+	return r
+}
+
+// UpdatePodFromYaml updates a Pod from YAML definition, handling name and namespace changes
+func (c *clusterService) UpdatePodFromYaml(contextName, yamlContent, originalName, originalNamespace string) types.PodResponse {
+	r := types.NewPodResponse()
+
+	// Get the client set for the context
+	cs, err := c.conf.GetClientSet(contextName)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	// Decode YAML to Kubernetes object
+	decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
+	obj, gvk, err := decode([]byte(yamlContent), nil, nil)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to parse YAML: %v", err)
+		return r
+	}
+
+	// Check if the object is a Pod
+	if gvk.Kind != "Pod" {
+		r.Msg = "YAML does not define a Pod resource"
+		return r
+	}
+
+	pod, ok := obj.(*corev1.Pod)
+	if !ok {
+		r.Msg = "Failed to convert object to Pod"
+		return r
+	}
+
+	// Ensure namespace is set
+	newNamespace := pod.Namespace
+	if newNamespace == "" {
+		newNamespace = "default"
+		pod.Namespace = newNamespace
+	}
+
+	newName := pod.Name
+
+	// Check if name or namespace changed
+	nameChanged := newName != originalName
+	namespaceChanged := newNamespace != originalNamespace
+
+	// Get the original pod client
+	originalPodsClient := cs.CoreV1().Pods(originalNamespace)
+
+	// Get the current pod to check if it exists
+	_, err = originalPodsClient.Get(c.ctx, originalName, metav1.GetOptions{})
+	if err != nil {
+		if errors.IsNotFound(err) {
+			r.Msg = fmt.Sprintf("Original pod %s not found in namespace %s", originalName, originalNamespace)
+		} else {
+			r.Msg = fmt.Sprintf("Failed to get original pod: %v", err)
+		}
+		return r
+	}
+
+	// If name or namespace changed, check if the new pod already exists
+	if nameChanged || namespaceChanged {
+		newPodsClient := cs.CoreV1().Pods(newNamespace)
+		_, err = newPodsClient.Get(c.ctx, newName, metav1.GetOptions{})
+		if err == nil {
+			// Pod with new name/namespace already exists
+			r.Msg = fmt.Sprintf("Pod %s already exists in namespace %s", newName, newNamespace)
+			return r
+		} else if !errors.IsNotFound(err) {
+			// Some other error occurred
+			r.Msg = fmt.Sprintf("Error checking for existing pod: %v", err)
+			return r
+		}
+		// If we get here, the new pod doesn't exist, which is what we want
+	}
+
+	err = originalPodsClient.Delete(c.ctx, originalName, kubernetesDeleteOptions)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to delete original pod: %v", err)
+		return r
+	}
+
+	// Wait for the original pod to be deleted
+	// TODO(rene): move timeout duration to config option
+	err = wait.PollImmediate(time.Second, time.Second*120, func() (bool, error) {
+		_, err := originalPodsClient.Get(c.ctx, originalName, metav1.GetOptions{})
+		if errors.IsNotFound(err) {
+			return true, nil
+		}
+		if err != nil {
+			return false, err
+		}
+		return false, nil
+	})
+
+	if err != nil {
+		r.Msg = fmt.Sprintf("Timed out waiting for pod deletion: %v", err)
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	// Remove fields that shouldn't be set on creation
+	pod.ResourceVersion = ""
+	pod.UID = ""
+	pod.CreationTimestamp = metav1.Time{}
+	pod.Status = corev1.PodStatus{}
+
+	// Create the pod with the updated spec
+	newPodsClient := cs.CoreV1().Pods(newNamespace)
+	createdPod, err := newPodsClient.Create(c.ctx, pod, metav1.CreateOptions{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to create updated pod: %v", err)
+		c.logger.Error(r.Msg)
+		return r
+	}
+
+	r.Data = *createdPod
+	r.Success = true
+
+	if nameChanged || namespaceChanged {
+		r.Msg = fmt.Sprintf("Pod renamed from %s/%s to %s/%s",
+			originalNamespace, originalName, newNamespace, newName)
+	} else {
+		r.Msg = fmt.Sprintf("Pod %s updated successfully in namespace %s", newName, newNamespace)
+	}
+
+	return r
+}

diff --git a/backend/services/http_service.go b/backend/services/http_service.go
line changes: +603/-0
index 0000000..ce18147
--- /dev/null
+++ b/backend/services/http_service.go
@@ -0,0 +1,603 @@
+package services
+
+import (
+	"bufio"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"kd/backend/config"
+	"kd/backend/logwrap"
+	"log"
+	"net/http"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/gorilla/websocket"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/kubernetes/scheme"
+	"k8s.io/client-go/tools/remotecommand"
+)
+
+type httpsrvService struct {
+	ctx    context.Context
+	conf   config.Config
+	logger *logwrap.LogWrap
+	srv    *http.Server
+}
+
+var httpsrv *httpsrvService
+var onceHttpsrv sync.Once
+
+func Httpsrv() *httpsrvService {
+	if httpsrv == nil {
+		onceHttpsrv.Do(func() {
+			httpsrv = &httpsrvService{}
+		})
+	}
+	return httpsrv
+}
+
+func (h *httpsrvService) Start(ctx context.Context, conf config.Config, logger *logwrap.LogWrap) {
+	h.ctx = ctx
+	h.conf = conf
+	h.logger = logger
+	h.startHTTPServer(8081)
+}
+
+func (h *httpsrvService) startHTTPServer(port int) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/api/pods/exec", h.HandlePodExecWebSocket) // Updated handler name
+	mux.HandleFunc("/api/pods/logs", h.HandlePodLogs)
+	mux.HandleFunc("/api/deployments/logs", h.HandleDeploymentLogs)
+	h.srv = &http.Server{
+		Addr:    fmt.Sprintf("127.0.0.1:%d", port),
+		Handler: mux,
+	}
+
+	go func() {
+		if err := h.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+			h.logger.Error(fmt.Sprintf("HTTP server error: %v\n", err))
+		}
+	}()
+}
+
+// HandlePodLogs handles the WebSocket connection for streaming pod logs
+func (h *httpsrvService) HandlePodLogs(w http.ResponseWriter, r *http.Request) {
+	// Get query parameters
+	contextName := r.URL.Query().Get("contextName")
+	namespace := r.URL.Query().Get("namespace")
+	pod := r.URL.Query().Get("name")
+	container := r.URL.Query().Get("container")
+	tailLinesStr := r.URL.Query().Get("tailLines")
+	stripColors := r.URL.Query().Get("stripColors") != "false" // Default to true
+
+	// Validate parameters
+	if contextName == "" || namespace == "" || pod == "" {
+		http.Error(w, "Missing required parameters: cluster, namespace, and name are required", http.StatusBadRequest)
+		return
+	}
+
+	// Parse tailLines parameter
+	var tailLines *int64
+	if tailLinesStr != "" {
+		tl, err := strconv.ParseInt(tailLinesStr, 10, 64)
+		if err != nil {
+			http.Error(w, fmt.Sprintf("Invalid tailLines parameter: %v", err), http.StatusBadRequest)
+			return
+		}
+		tailLines = &tl
+	} else {
+		// Default to 100 lines if not specified
+		defaultTailLines := int64(100)
+		tailLines = &defaultTailLines
+	}
+
+	// Upgrade connection to WebSocket
+	upgrader := websocket.Upgrader{
+		CheckOrigin: func(r *http.Request) bool {
+			return true
+		},
+	}
+
+	// Upgrade connection to WebSocket
+	ws, err := upgrader.Upgrade(w, r, nil)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Failed to upgrade connection: %v", err), http.StatusInternalServerError)
+		return
+	}
+	defer ws.Close()
+
+	// Create pod logs request
+	podLogOpts := &corev1.PodLogOptions{
+		Follow:     true,
+		TailLines:  tailLines,
+		Timestamps: true,
+	}
+
+	// Only set container if it's provided
+	if container != "" {
+		podLogOpts.Container = container
+	}
+
+	cs, err := h.conf.GetClientSet(contextName)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Failed to get context: %v", err), http.StatusInternalServerError)
+		return
+	}
+
+	req := cs.CoreV1().Pods(namespace).GetLogs(pod, podLogOpts)
+
+	// Get the log stream
+	stream, err := req.Stream(context.Background())
+	if err != nil {
+		ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("Error getting log stream: %v", err)))
+		return
+	}
+	defer stream.Close()
+
+	// Compile regex for ANSI color codes once
+	ansiRegex := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
+
+	// Read the logs
+	reader := bufio.NewReader(stream)
+	for {
+		line, err := reader.ReadBytes('\n')
+		if err != nil {
+			if err != io.EOF {
+				ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("Error reading logs: %v", err)))
+			}
+			return
+		}
+
+		// Strip ANSI color codes if requested
+		if stripColors {
+			line = ansiRegex.ReplaceAll(line, []byte{})
+		}
+
+		// Send the line without adding extra newlines
+		err = ws.WriteMessage(websocket.TextMessage, line)
+		if err != nil {
+			log.Printf("Error sending log line via WebSocket: %v", err)
+			return
+		}
+	}
+}
+
+// Helper function to create pointer to int64
+func int64Ptr(i int64) *int64 {
+	return &i
+}
+
+// TerminalSession represents a terminal session with size queue support
+type TerminalSession struct {
+	ws       *websocket.Conn
+	sizeChan chan remotecommand.TerminalSize
+	doneChan chan struct{}
+}
+
+// Read reads data from the WebSocket
+func (t *TerminalSession) Read(p []byte) (int, error) {
+	_, message, err := t.ws.ReadMessage()
+	if err != nil {
+		return 0, err
+	}
+
+	// Check if this is a terminal resize message
+	if len(message) >= 3 && message[0] == 1 {
+		// This is a resize message
+		rows := uint16(message[1])
+		cols := uint16(message[2])
+
+		// Log the resize event
+		log.Printf("Terminal resize: %dx%d", cols, rows)
+
+		// Send the new size to the terminal
+		t.sizeChan <- remotecommand.TerminalSize{Width: cols, Height: rows}
+		return 0, nil
+	}
+
+	// Check if this is a JSON resize message (for backward compatibility)
+	if len(message) > 0 && message[0] == '{' {
+		var resizeMsg struct {
+			Type string `json:"type"`
+			Cols int    `json:"cols"`
+			Rows int    `json:"rows"`
+		}
+
+		if err := json.Unmarshal(message, &resizeMsg); err == nil && resizeMsg.Type == "resize" {
+			// This is a resize message
+			log.Printf("Terminal resize (JSON): %dx%d", resizeMsg.Cols, resizeMsg.Rows)
+			t.sizeChan <- remotecommand.TerminalSize{
+				Width:  uint16(resizeMsg.Cols),
+				Height: uint16(resizeMsg.Rows),
+			}
+			return 0, nil
+		}
+	}
+
+	// Regular data message
+	copy(p, message)
+	return len(message), nil
+}
+
+// Write writes data to the WebSocket
+func (t *TerminalSession) Write(p []byte) (int, error) {
+	err := t.ws.WriteMessage(websocket.BinaryMessage, p)
+	if err != nil {
+		return 0, err
+	}
+	return len(p), nil
+}
+
+// Next returns the next terminal size
+func (t *TerminalSession) Next() *remotecommand.TerminalSize {
+	select {
+	case size := <-t.sizeChan:
+		return &size
+	case <-t.doneChan:
+		return nil
+	}
+}
+
+// Close closes the terminal session
+func (t *TerminalSession) Close() {
+	close(t.doneChan)
+}
+
+// HandlePodExecWebSocket handles WebSocket connections for pod exec
+func (h *httpsrvService) HandlePodExecWebSocket(w http.ResponseWriter, r *http.Request) {
+	// Extract query parameters
+	contextName := r.URL.Query().Get("contextName")
+	namespace := r.URL.Query().Get("namespace")
+	podName := r.URL.Query().Get("pod")
+	containerName := r.URL.Query().Get("container")
+
+	// Log the request
+	h.logger.Debug(fmt.Sprintf("Terminal request: context=%s, namespace=%s, pod=%s, container=%s",
+		contextName, namespace, podName, containerName))
+
+	// Validate parameters
+	if contextName == "" || namespace == "" || podName == "" {
+		http.Error(w, "Missing required parameters", http.StatusBadRequest)
+		return
+	}
+
+	// Get the client for the current context
+	clientset, err := h.conf.GetClientSet(contextName)
+	if err != nil {
+		errMsg := fmt.Sprintf("Failed to get client for context %s: %v", contextName, err)
+		h.logger.Error(errMsg)
+		http.Error(w, errMsg, http.StatusInternalServerError)
+		return
+	}
+
+	// Get the rest config
+	restConfig, err := h.conf.GetRestConfig(contextName)
+	if err != nil {
+		errMsg := fmt.Sprintf("Failed to get REST config for context %s: %v", contextName, err)
+		h.logger.Error(errMsg)
+		http.Error(w, errMsg, http.StatusInternalServerError)
+		return
+	}
+
+	// Upgrade HTTP connection to WebSocket
+	upgrader := websocket.Upgrader{
+		ReadBufferSize:  1024,
+		WriteBufferSize: 1024,
+		CheckOrigin: func(r *http.Request) bool {
+			return true // Allow all origins in development
+		},
+	}
+
+	ws, err := upgrader.Upgrade(w, r, nil)
+	if err != nil {
+		errMsg := fmt.Sprintf("Failed to upgrade connection: %v", err)
+		h.logger.Error(errMsg)
+		http.Error(w, errMsg, http.StatusInternalServerError)
+		return
+	}
+	defer ws.Close()
+
+	// Create terminal session
+	session := &TerminalSession{
+		ws:       ws,
+		sizeChan: make(chan remotecommand.TerminalSize),
+		doneChan: make(chan struct{}),
+	}
+	defer session.Close()
+
+	// Set up exec request
+	req := clientset.CoreV1().RESTClient().Post().
+		Resource("pods").
+		Name(podName).
+		Namespace(namespace).
+		SubResource("exec")
+
+	// Set query parameters
+	req.VersionedParams(&corev1.PodExecOptions{
+		Container: containerName,
+		Command:   []string{"/bin/sh", "-c", "[ -x /bin/bash ] && exec /bin/bash || exec /bin/sh"},
+		Stdin:     true,
+		Stdout:    true,
+		Stderr:    true,
+		TTY:       true,
+	}, scheme.ParameterCodec)
+
+	// Create a context with timeout
+	ctx, cancel := context.WithTimeout(h.ctx, 10*60*time.Second) // 10 minutes
+	defer cancel()
+
+	// Create SPDY executor
+	exec, err := remotecommand.NewSPDYExecutor(restConfig, "POST", req.URL())
+	if err != nil {
+		errMsg := fmt.Sprintf("Failed to create executor: %v", err)
+		h.logger.Error(errMsg)
+		ws.WriteMessage(websocket.TextMessage, []byte(errMsg))
+		return
+	}
+
+	// Stream terminal session
+	err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{
+		Stdin:             session,
+		Stdout:            session,
+		Stderr:            session,
+		Tty:               true,
+		TerminalSizeQueue: session,
+	})
+
+	if err != nil {
+		errMsg := fmt.Sprintf("Error executing command: %v", err)
+		h.logger.Error(errMsg)
+		ws.WriteMessage(websocket.TextMessage, []byte(errMsg))
+	}
+}
+
+// HandleDeploymentLogs streams logs from all pods and all containers in a deployment
+func (h *httpsrvService) HandleDeploymentLogs(w http.ResponseWriter, r *http.Request) {
+	// Get query parameters
+	contextName := r.URL.Query().Get("contextName")
+	namespace := r.URL.Query().Get("namespace")
+	deploymentName := r.URL.Query().Get("name")
+
+	// Validate parameters
+	if contextName == "" || namespace == "" || deploymentName == "" {
+		http.Error(w, "Missing required parameters", http.StatusBadRequest)
+		return
+	}
+
+	// Upgrade connection to WebSocket
+	upgrader := websocket.Upgrader{
+		CheckOrigin: func(r *http.Request) bool {
+			return true
+		},
+	}
+
+	ws, err := upgrader.Upgrade(w, r, nil)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Failed to upgrade connection: %v", err), http.StatusInternalServerError)
+		return
+	}
+	defer ws.Close()
+
+	// Create a mutex to synchronize access to the WebSocket connection
+	var wsMutex sync.Mutex
+
+	// Safe write function that uses the mutex
+	safeWrite := func(messageType int, data []byte) error {
+		wsMutex.Lock()
+		defer wsMutex.Unlock()
+		return ws.WriteMessage(messageType, data)
+	}
+
+	// Send initial message
+	safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Getting logs for deployment %s in namespace %s\n", deploymentName, namespace)))
+
+	cs, err := h.conf.GetClientSet(contextName)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Failed to get client set: %v", err), http.StatusInternalServerError)
+		return
+	}
+
+	labelSelector := fmt.Sprintf("app=%s", deploymentName)
+	pods, err := cs.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
+		LabelSelector: labelSelector,
+	})
+
+	if err != nil {
+		safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Error getting pods: %v\n", err)))
+		return
+	}
+
+	if len(pods.Items) == 0 {
+		// Try another common label pattern
+		labelSelector = fmt.Sprintf("app.kubernetes.io/name=%s", deploymentName)
+		pods, err = cs.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
+			LabelSelector: labelSelector,
+		})
+
+		if err != nil {
+			safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Error getting pods with alternate label: %v\n", err)))
+			return
+		}
+	}
+
+	// If still no pods found, try matching by name prefix
+	if len(pods.Items) == 0 {
+		allPods, err := cs.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{})
+		if err != nil {
+			safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Error getting all pods: %v\n", err)))
+			return
+		}
+
+		// Filter pods that have the deployment name as a prefix
+		for _, pod := range allPods.Items {
+			if strings.HasPrefix(pod.Name, deploymentName+"-") {
+				pods.Items = append(pods.Items, pod)
+			}
+		}
+	}
+
+	if len(pods.Items) == 0 {
+		safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("No pods found for deployment %s\n", deploymentName)))
+		return
+	}
+
+	// Send message about found pods
+	safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Found %d pods for deployment %s\n", len(pods.Items), deploymentName)))
+
+	// Create wait group to wait for all goroutines to finish
+	var wg sync.WaitGroup
+
+	// Create a context that can be canceled
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	// Create a channel to signal when the WebSocket is closed
+	done := make(chan struct{})
+
+	// Monitor WebSocket connection
+	go func() {
+		defer close(done)
+		for {
+			_, _, err := ws.ReadMessage()
+			if err != nil {
+				// WebSocket closed or error
+				cancel() // Cancel all log streaming
+				return
+			}
+		}
+	}()
+
+	// Stream logs from each pod and each container
+	for _, pod := range pods.Items {
+		// For each container in the pod
+		for _, container := range pod.Spec.Containers {
+			wg.Add(1)
+
+			go func(pod corev1.Pod, containerName string) {
+				defer wg.Done()
+
+				podName := pod.Name
+
+				// Send message about which pod/container we're getting logs from
+				safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("\n=== Logs from pod %s, container %s ===\n", podName, containerName)))
+
+				// Create pod logs request
+				podLogOpts := &corev1.PodLogOptions{
+					Container:  containerName,
+					Follow:     true,
+					TailLines:  int64Ptr(100),
+					Timestamps: true,
+				}
+
+				req := cs.CoreV1().Pods(namespace).GetLogs(podName, podLogOpts)
+				stream, err := req.Stream(ctx)
+				if err != nil {
+					safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Error getting log stream for pod %s, container %s: %v\n", podName, containerName, err)))
+					return
+				}
+				defer stream.Close()
+
+				// Read the logs
+				reader := bufio.NewReader(stream)
+				for {
+					select {
+					case <-ctx.Done():
+						// Context was canceled (WebSocket closed)
+						return
+					case <-done:
+						// WebSocket closed
+						return
+					default:
+						line, err := reader.ReadBytes('\n')
+						if err != nil {
+							if err != io.EOF {
+								safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Error reading logs from pod %s, container %s: %v\n", podName, containerName, err)))
+							}
+							return
+						}
+
+						// Prefix the line with the pod and container name for clarity
+						prefixedLine := fmt.Sprintf("[%s/%s] %s", podName, containerName, string(line))
+
+						// Send the line
+						err = safeWrite(websocket.TextMessage, []byte(prefixedLine))
+						if err != nil {
+							// WebSocket error, stop streaming
+							return
+						}
+					}
+				}
+			}(pod, container.Name)
+		}
+
+		// Also check for init containers
+		for _, container := range pod.Spec.InitContainers {
+			wg.Add(1)
+
+			go func(pod corev1.Pod, containerName string) {
+				defer wg.Done()
+
+				podName := pod.Name
+
+				// Send message about which pod/container we're getting logs from
+				safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("\n=== Logs from pod %s, init container %s ===\n", podName, containerName)))
+
+				// Create pod logs request
+				podLogOpts := &corev1.PodLogOptions{
+					Container:  containerName,
+					Follow:     false, // Init containers don't need follow as they're completed
+					TailLines:  int64Ptr(100),
+					Timestamps: true,
+				}
+
+				req := cs.CoreV1().Pods(namespace).GetLogs(podName, podLogOpts)
+				stream, err := req.Stream(ctx)
+				if err != nil {
+					safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Error getting log stream for pod %s, init container %s: %v\n", podName, containerName, err)))
+					return
+				}
+				defer stream.Close()
+
+				// Read the logs
+				reader := bufio.NewReader(stream)
+				for {
+					select {
+					case <-ctx.Done():
+						// Context was canceled (WebSocket closed)
+						return
+					case <-done:
+						// WebSocket closed
+						return
+					default:
+						line, err := reader.ReadBytes('\n')
+						if err != nil {
+							if err != io.EOF {
+								safeWrite(websocket.TextMessage, []byte(fmt.Sprintf("Error reading logs from pod %s, init container %s: %v\n", podName, containerName, err)))
+							}
+							return
+						}
+
+						// Prefix the line with the pod and container name for clarity
+						prefixedLine := fmt.Sprintf("[%s/%s] %s", podName, containerName, string(line))
+
+						// Send the line
+						err = safeWrite(websocket.TextMessage, []byte(prefixedLine))
+						if err != nil {
+							// WebSocket error, stop streaming
+							return
+						}
+					}
+				}
+			}(pod, container.Name)
+		}
+	}
+
+	// Wait for all log streaming to finish
+	wg.Wait()
+}

diff --git a/backend/services/persistent_volume_claims_service.go b/backend/services/persistent_volume_claims_service.go
line changes: +169/-0
index 0000000..df27f04
--- /dev/null
+++ b/backend/services/persistent_volume_claims_service.go
@@ -0,0 +1,169 @@
+package services
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"kd/backend/config"
+	"kd/backend/logwrap"
+	"kd/backend/types"
+	"sync"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	"k8s.io/kubectl/pkg/describe"
+	describecmd "k8s.io/kubectl/pkg/describe"
+	"k8s.io/kubectl/pkg/scheme"
+)
+
+type persistentVolumeClaimsService struct {
+	ctx    context.Context
+	conf   config.Config
+	logger *logwrap.LogWrap
+}
+
+var persistentVolumeClaims *persistentVolumeClaimsService
+var oncePersistentVolumeClaims sync.Once
+
+func PersistentVolumeClaims() *persistentVolumeClaimsService {
+	if persistentVolumeClaims == nil {
+		oncePersistentVolumeClaims.Do(func() {
+			persistentVolumeClaims = &persistentVolumeClaimsService{}
+		})
+	}
+	return persistentVolumeClaims
+}
+
+func (p *persistentVolumeClaimsService) Start(ctx context.Context, conf config.Config, logger *logwrap.LogWrap) {
+	p.ctx = ctx
+	p.conf = conf
+	p.logger = logger
+}
+
+func (p *persistentVolumeClaimsService) getPersistentVolumeClaimFromDefinition(definition any) (*corev1.PersistentVolumeClaim, error) {
+	var data []byte
+	var err error
+
+	switch v := definition.(type) {
+	case string:
+		data = []byte(v)
+	case []byte:
+		data = v
+	default:
+		data, err = json.Marshal(definition)
+		if err != nil {
+			return nil, fmt.Errorf("failed to marshal definition: %w", err)
+		}
+	}
+
+	decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
+	obj, gvk, err := decode(data, nil, nil)
+	if err != nil {
+		return nil, err
+	}
+	if gvk.Kind != "PersistentVolumeClaim" {
+		return nil, errors.New("definition does not define a PersistentVolumeClaim resource")
+	}
+
+	persistentVolumeClaim, ok := obj.(*corev1.PersistentVolumeClaim)
+	if !ok {
+		return nil, errors.New("failed to convert object to PersistentVolumeClaim")
+	}
+
+	return persistentVolumeClaim, nil
+}
+
+func (p *persistentVolumeClaimsService) Create(context string, definition any) types.PersistentVolumeClaimResponse {
+	r := types.NewPersistentVolumeClaimResponse()
+
+	cs, err := p.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find context name: %s", err.Error())
+		return r
+	}
+
+	persistentVolumeClaim, err := p.getPersistentVolumeClaimFromDefinition(definition)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to get persistent volume claim from definition: %s", err.Error())
+		return r
+	}
+
+	if _, err := cs.CoreV1().PersistentVolumeClaims(persistentVolumeClaim.ObjectMeta.GetNamespace()).Create(p.ctx, persistentVolumeClaim, metav1.CreateOptions{}); err != nil {
+		r.Msg = fmt.Sprintf("Failed to create resource: %s", err.Error())
+		return r
+	}
+
+	r.Msg = fmt.Sprintf("Persistent Volume Claim created: %s", persistentVolumeClaim.GetName())
+	r.Success = true
+
+	return r
+}
+
+func (p *persistentVolumeClaimsService) Delete(context, namespace, name string) types.Response {
+	r := types.NewResponse()
+
+	cs, err := p.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find context name: %s", err.Error())
+		return r
+	}
+
+	if err := cs.CoreV1().PersistentVolumeClaims(namespace).Delete(p.ctx, name, kubernetesDeleteOptions); err != nil {
+		r.Msg = fmt.Sprintf("Failed to delete Persistent Volume Claim: %s", err.Error())
+		return r
+	}
+
+	r.Msg = fmt.Sprintf("Persistent Volume Claim deleted: %s", name)
+	r.Success = true
+	return r
+}
+
+func (p *persistentVolumeClaimsService) List(context, namespace string) types.PersistentVolumeClaimsResponse {
+	r := types.NewPersistentVolumeClaimsResponse()
+
+	cs, err := p.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	persistentVolumeClaimsList, err := cs.CoreV1().PersistentVolumeClaims(namespace).List(p.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = "Failed to list Persistent Volume Claims"
+		return r
+	}
+
+	r.Data = persistentVolumeClaimsList.Items
+	r.Success = true
+	return r
+}
+
+func (p *persistentVolumeClaimsService) Describe(context, namespace, name string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := p.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find rest config name: %s", err.Error())
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "", Kind: "PersistentVolumeClaim"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = fmt.Sprintf("Failed to find describer: %s", err.Error())
+		return r
+	}
+
+	out, err := d.Describe(namespace, name, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to describe resource: %s", err.Error())
+		return r
+	}
+
+	r.Data = out
+	r.Success = true
+	return r
+}

diff --git a/backend/services/persistent_volumes_service.go b/backend/services/persistent_volumes_service.go
line changes: +166/-0
index 0000000..e658c6e
--- /dev/null
+++ b/backend/services/persistent_volumes_service.go
@@ -0,0 +1,166 @@
+package services
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"kd/backend/config"
+	"kd/backend/logwrap"
+	"kd/backend/types"
+	"sync"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	"k8s.io/kubectl/pkg/describe"
+	describecmd "k8s.io/kubectl/pkg/describe"
+	"k8s.io/kubectl/pkg/scheme"
+)
+
+type persistentVolumesService struct {
+	ctx    context.Context
+	conf   config.Config
+	logger *logwrap.LogWrap
+}
+
+var persistentVolumes *persistentVolumesService
+var oncePersistentVolumes sync.Once
+
+func PersistentVolumes() *persistentVolumesService {
+	if persistentVolumes == nil {
+		oncePersistentVolumes.Do(func() {
+			persistentVolumes = &persistentVolumesService{}
+		})
+	}
+	return persistentVolumes
+}
+
+func (p *persistentVolumesService) Start(ctx context.Context, conf config.Config, logger *logwrap.LogWrap) {
+	p.ctx = ctx
+	p.conf = conf
+	p.logger = logger
+}
+
+func (p *persistentVolumesService) List(context string) types.PersistentVolumesResponse {
+	r := types.NewPersistentVolumesResponse()
+
+	cs, err := p.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	persistentVolumesList, err := cs.CoreV1().PersistentVolumes().List(p.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = "Failed to list Persistent Volumes"
+		return r
+	}
+
+	r.Data = persistentVolumesList.Items
+	r.Success = true
+	return r
+}
+
+func (p *persistentVolumesService) Describe(context, namespace, name string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := p.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find rest config name: %s", err.Error())
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "", Kind: "PersistentVolume"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = fmt.Sprintf("Failed to find describer: %s", err.Error())
+		return r
+	}
+
+	out, err := d.Describe(namespace, name, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to describe resource: %s", err.Error())
+		return r
+	}
+
+	r.Data = out
+	r.Success = true
+	return r
+}
+
+func (p *persistentVolumesService) Create(context string, definition any) types.Response {
+	r := types.NewResponse()
+
+	cs, err := p.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find context name: %s", err.Error())
+		return r
+	}
+
+	persistentVolume, err := p.getPersistentVolumeFromDefinition(definition)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to get volume from definition: %s", err.Error())
+		return r
+	}
+
+	if _, err := cs.CoreV1().PersistentVolumes().Create(p.ctx, persistentVolume, metav1.CreateOptions{}); err != nil {
+		r.Msg = fmt.Sprintf("Failed to create resource: %s", err.Error())
+		return r
+	}
+
+	r.Msg = fmt.Sprintf("Persistent Volume created: %s", persistentVolume.GetName())
+	r.Success = true
+	return r
+}
+
+func (p *persistentVolumesService) getPersistentVolumeFromDefinition(definition any) (*corev1.PersistentVolume, error) {
+	var data []byte
+	var err error
+
+	switch v := definition.(type) {
+	case string:
+		data = []byte(v)
+	case []byte:
+		data = v
+	default:
+		data, err = json.Marshal(definition)
+		if err != nil {
+			return nil, fmt.Errorf("failed to marshal definition: %w", err)
+		}
+	}
+
+	decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
+	obj, gvk, err := decode(data, nil, nil)
+	if err != nil {
+		return nil, err
+	}
+	if gvk.Kind != "PersistentVolume" {
+		return nil, errors.New("definition does not define a PersistentVolume resource")
+	}
+
+	persistentVolume, ok := obj.(*corev1.PersistentVolume)
+	if !ok {
+		return nil, errors.New("failed to convert object to PersistentVolume")
+	}
+
+	return persistentVolume, nil
+}
+
+func (p *persistentVolumesService) Delete(context, name string) types.Response {
+	r := types.NewResponse()
+	cs, err := p.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+	client := cs.CoreV1().PersistentVolumes()
+	if err := client.Delete(p.ctx, name, kubernetesDeleteOptions); err != nil {
+		r.Msg = fmt.Sprintf("Failed to delete Persistent Volume: %s", err.Error())
+		return r
+	}
+	r.Msg = fmt.Sprintf("Persistent Volume deleted: %s", name)
+	r.Success = true
+	return r
+}

diff --git a/backend/services/secrets_service.go b/backend/services/secrets_service.go
line changes: +332/-0
index 0000000..74403b2
--- /dev/null
+++ b/backend/services/secrets_service.go
@@ -0,0 +1,332 @@
+package services
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"kd/backend/config"
+	"kd/backend/logwrap"
+	"kd/backend/types"
+	"strings"
+	"sync"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	k8types "k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/util/retry"
+	"k8s.io/kubectl/pkg/describe"
+	describecmd "k8s.io/kubectl/pkg/describe"
+	"k8s.io/kubectl/pkg/scheme"
+)
+
+type secretsService struct {
+	ctx    context.Context
+	conf   config.Config
+	logger *logwrap.LogWrap
+}
+
+var secrets *secretsService
+var onceSecrets sync.Once
+
+func Secrets() *secretsService {
+	if secrets == nil {
+		onceSecrets.Do(func() {
+			secrets = &secretsService{}
+		})
+	}
+	return secrets
+}
+
+func (s *secretsService) Start(ctx context.Context, conf config.Config, logger *logwrap.LogWrap) {
+	s.ctx = ctx
+	s.conf = conf
+	s.logger = logger
+}
+
+func (s *secretsService) List(context, namespace string) types.SecretsResponse {
+	r := types.NewSecretsResponse()
+
+	cs, err := s.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	secretsList, err := cs.CoreV1().Secrets(namespace).List(s.ctx, metav1.ListOptions{})
+	if err != nil {
+		r.Msg = "Failed to list Secrets"
+		return r
+	}
+
+	r.Data = secretsList.Items
+	r.Success = true
+	return r
+}
+
+func (s *secretsService) Describe(context, namespace, name string) types.DescribeResponse {
+	r := types.NewDescribeResponse()
+
+	rc, err := s.conf.GetRestConfig(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find rest config name: %s", err.Error())
+		return r
+	}
+
+	gk := schema.GroupKind{Group: "", Kind: "Secret"}
+	d, ok := describecmd.DescriberFor(gk, rc)
+	if !ok {
+		r.Msg = fmt.Sprintf("Failed to find describer: %s", err.Error())
+		return r
+	}
+
+	out, err := d.Describe(namespace, name, describe.DescriberSettings{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to describe resource: %s", err.Error())
+		return r
+	}
+
+	r.Data = out
+	r.Success = true
+	return r
+}
+
+func (s *secretsService) getSecretFromCustomSecret(secretMap map[string]any) (*corev1.Secret, error) {
+	secret := &corev1.Secret{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "v1",
+			Kind:       "Secret",
+		},
+		ObjectMeta: metav1.ObjectMeta{},
+		Type:       corev1.SecretTypeOpaque, // Default type
+		Data:       map[string][]byte{},
+		StringData: map[string]string{},
+	}
+
+	// Extract metadata
+	if metadata, ok := secretMap["metadata"].(map[string]interface{}); ok {
+		// Set name
+		if name, ok := metadata["name"].(string); ok {
+			secret.ObjectMeta.Name = name
+		}
+
+		// Set namespace
+		if namespace, ok := metadata["namespace"].(string); ok {
+			secret.ObjectMeta.Namespace = namespace
+		}
+
+		// Set labels
+		if labels, ok := metadata["labels"].(map[string]interface{}); ok {
+			secret.ObjectMeta.Labels = make(map[string]string)
+			for key, value := range labels {
+				if strValue, ok := value.(string); ok {
+					secret.ObjectMeta.Labels[key] = strValue
+				}
+			}
+		}
+
+		// Set annotations
+		if annotations, ok := metadata["annotations"].(map[string]interface{}); ok {
+			secret.ObjectMeta.Annotations = make(map[string]string)
+			for key, value := range annotations {
+				if strValue, ok := value.(string); ok {
+					secret.ObjectMeta.Annotations[key] = strValue
+				}
+			}
+		}
+	}
+
+	// Extract type
+	if secretType, ok := secretMap["type"].(string); ok {
+		secret.Type = corev1.SecretType(secretType)
+	}
+
+	// Extract data (base64 encoded)
+	if data, ok := secretMap["data"].(map[string]interface{}); ok {
+		for key, value := range data {
+			if strValue, ok := value.(string); ok {
+				// Data in k8s secrets is base64 encoded
+				secret.Data[key] = []byte(strValue)
+			}
+		}
+	}
+
+	// Extract stringData (not base64 encoded)
+	if stringData, ok := secretMap["stringData"].(map[string]interface{}); ok {
+		for key, value := range stringData {
+			if strValue, ok := value.(string); ok {
+				secret.StringData[key] = strValue
+			}
+		}
+	}
+
+	return secret, nil
+}
+
+func (s *secretsService) Delete(context, namespace, name string) types.Response {
+	r := types.NewResponse()
+	cs, err := s.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	client := cs.CoreV1().Secrets(namespace)
+	if err := client.Delete(s.ctx, name, kubernetesDeleteOptions); err != nil {
+		r.Msg = fmt.Sprintf("Failed to delete Secret: %s", name)
+		return r
+	}
+	r.Msg = fmt.Sprintf("Secret deleted: %s", name)
+	r.Success = true
+	return r
+}
+
+func (s *secretsService) Get(context, namespace, name string) types.SecretResponse {
+	r := types.NewSecretResponse()
+	cs, err := s.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+	secret, err := cs.CoreV1().Secrets(namespace).Get(s.ctx, name, metav1.GetOptions{})
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to get Secret: %s", err.Error())
+		return r
+	}
+	r.Data = *secret
+	r.Success = true
+	return r
+}
+
+func (s *secretsService) Update(context, input, originalNamespace, originalName string) types.SecretResponse {
+	r := types.NewSecretResponse()
+
+	cs, err := s.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = "Failed to find context name"
+		return r
+	}
+
+	decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
+	obj, gvk, err := decode([]byte(input), nil, nil)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to parse YAML: %v", err)
+		return r
+	}
+
+	if gvk.Kind != "Secret" {
+		r.Msg = "YAML does not define a Secret resource"
+		return r
+	}
+
+	secret, ok := obj.(*corev1.Secret)
+	if !ok {
+		r.Msg = "Failed to convert YAML to Secret"
+		return r
+	}
+
+	if strings.ToLower(secret.GetName()) == strings.ToLower(originalName) && strings.ToLower(secret.GetNamespace()) == strings.ToLower(originalNamespace) {
+		b, err := json.Marshal(obj)
+		if err != nil {
+			r.Msg = fmt.Sprintf("Failed to convert object to JSON: %v", err)
+			return r
+		}
+
+		if err := s.patch(cs, originalNamespace, originalName, b); err != nil {
+			r.Msg = fmt.Sprintf("Failed to patch Secret: %v", err)
+			return r
+		}
+	} else {
+		originalClient := cs.CoreV1().Secrets(originalNamespace)
+		client := cs.CoreV1().Secrets(secret.GetNamespace())
+		secret, err = client.Create(s.ctx, secret, metav1.CreateOptions{})
+		if err != nil {
+			r.Msg = fmt.Sprintf("Failed to create Secret: %v", err)
+			return r
+		}
+		if err = originalClient.Delete(s.ctx, originalName, kubernetesDeleteOptions); err != nil {
+			r.Msg = fmt.Sprintf("Failed to delete original Secret: %v", err)
+			return r
+		}
+		r.Data = *secret
+	}
+
+	r.Msg = fmt.Sprintf("Updated Secret: %s", originalName)
+	r.Success = true
+	return r
+}
+
+func (s *secretsService) patch(clientset *kubernetes.Clientset, namespace, name string, patchData []byte) error {
+	return retry.RetryOnConflict(retry.DefaultRetry, func() error {
+		_, err := clientset.CoreV1().Secrets(namespace).Patch(
+			context.TODO(),
+			name,
+			k8types.StrategicMergePatchType,
+			patchData,
+			metav1.PatchOptions{},
+		)
+		return err
+	})
+}
+
+func (s *secretsService) Create(context string, input any) types.Response {
+	var secret *corev1.Secret
+	var err error
+
+	r := types.NewResponse()
+
+	switch t := input.(type) {
+	case map[string]any:
+		secret, err = s.getSecretFromCustomSecret(t)
+		if err != nil {
+			r.Msg = fmt.Sprintf("Failed to get resource from definition: %s", err.Error())
+			return r
+		}
+	case string:
+		secret, err = s.getSecretFromYamlWithCodec(t)
+		if err != nil {
+			r.Msg = fmt.Sprintf("Failed to create Secret from YAML: %s", err.Error())
+			return r
+		}
+	default:
+		r.Msg = "Input must be either a Secret definition object or YAML string"
+		return r
+	}
+
+	cs, err := s.conf.GetClientSet(context)
+	if err != nil {
+		r.Msg = fmt.Sprintf("Failed to find context name: %s", err.Error())
+		return r
+	}
+
+	namespace := secret.ObjectMeta.GetNamespace()
+	if _, err := cs.CoreV1().Secrets(namespace).Create(s.ctx, secret, metav1.CreateOptions{}); err != nil {
+		r.Msg = fmt.Sprintf("Failed to create resource: %s", err.Error())
+		return r
+	}
+
+	r.Msg = fmt.Sprintf("Secret created: %s", secret.GetName())
+	r.Success = true
+	return r
+}
+
+func (s *secretsService) getSecretFromYamlWithCodec(yamlContent string) (*corev1.Secret, error) {
+	decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
+	obj, gvk, err := decode([]byte(yamlContent), nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if gvk.Kind != "Secret" {
+		return nil, fmt.Errorf("YAML does not define a Secret resource, got: %s", gvk.Kind)
+	}
+
+	secret, ok := obj.(*corev1.Secret)
+	if !ok {
+		return nil, fmt.Errorf("failed to convert object to Secret")
+	}
+
+	return secret, nil
+}

diff --git a/backend/services/services.go b/backend/services/services.go
line changes: +10/-0
index 0000000..f5cfe40
--- /dev/null
+++ b/backend/services/services.go
@@ -0,0 +1,10 @@
+package services
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var kubernetesDeletePolicy = metav1.DeletePropagationForeground
+var kubernetesDeleteOptions = metav1.DeleteOptions{
+	PropagationPolicy: &kubernetesDeletePolicy,
+}

diff --git a/backend/types/cluster.go b/backend/types/cluster.go
line changes: +84/-0
index 0000000..959752c
--- /dev/null
+++ b/backend/types/cluster.go
@@ -0,0 +1,84 @@
+package types
+
+import (
+	corev1 "k8s.io/api/core/v1"
+)
+
+type ClustersResponse struct {
+	Success bool      `json:"success"`
+	Msg     string    `json:"msg"`
+	Data    []Cluster `json:"data"`
+}
+
+type Cluster struct {
+	ContextName string `json:"contextName"`
+}
+
+func NewClustersResponse() ClustersResponse {
+	r := ClustersResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []Cluster{},
+	}
+	return r
+}
+
+type PodsResponse struct {
+	Success bool         `json:"success"`
+	Msg     string       `json:"msg"`
+	Data    []corev1.Pod `json:"data"`
+}
+
+type PodResponse struct {
+	Success bool       `json:"success"`
+	Msg     string     `json:"msg"`
+	Data    corev1.Pod `json:"data"`
+}
+
+type Response struct {
+	Success bool   `json:"success"`
+	Msg     string `json:"msg"`
+	Data    string `json:"data"`
+}
+
+func NewResponse() Response {
+	return Response{}
+}
+
+type DescribeResponse struct {
+	Success bool   `json:"success"`
+	Msg     string `json:"msg"`
+	Data    string `json:"data"`
+}
+
+func NewDescribeResponse() DescribeResponse {
+	return DescribeResponse{}
+}
+
+type DescribePodResponse struct {
+	Success bool   `json:"success"`
+	Msg     string `json:"msg"`
+	Data    string `json:"data"`
+}
+
+func NewDescribePodResponse() DescribePodResponse {
+	return DescribePodResponse{}
+}
+
+func NewPodsResponse() PodsResponse {
+	r := PodsResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []corev1.Pod{},
+	}
+	return r
+}
+
+func NewPodResponse() PodResponse {
+	r := PodResponse{
+		Success: false,
+		Msg:     "",
+		Data:    corev1.Pod{},
+	}
+	return r
+}

diff --git a/backend/types/configmap.go b/backend/types/configmap.go
line changes: +43/-0
index 0000000..81acbb4
--- /dev/null
+++ b/backend/types/configmap.go
@@ -0,0 +1,43 @@
+package types
+
+import corev1 "k8s.io/api/core/v1"
+
+type ConfigMapsResponse struct {
+	Success bool               `json:"success"`
+	Msg     string             `json:"msg"`
+	Data    []corev1.ConfigMap `json:"data"`
+}
+
+type ConfigMapResponse struct {
+	Success bool             `json:"success"`
+	Msg     string           `json:"msg"`
+	Data    corev1.ConfigMap `json:"data"`
+}
+
+func NewConfigMapsResponse() ConfigMapsResponse {
+	r := ConfigMapsResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []corev1.ConfigMap{},
+	}
+	return r
+}
+
+func NewConfigMapResponse() ConfigMapResponse {
+	r := ConfigMapResponse{
+		Success: false,
+		Msg:     "",
+		Data:    corev1.ConfigMap{},
+	}
+	return r
+}
+
+type DescribeConfigMapResponse struct {
+	Success bool   `json:"success"`
+	Msg     string `json:"msg"`
+	Data    string `json:"data"`
+}
+
+func NewDescribeConfigMapResponse() DescribeConfigMapResponse {
+	return DescribeConfigMapResponse{}
+}

diff --git a/backend/types/deployment.go b/backend/types/deployment.go
line changes: +35/-0
index 0000000..b80f2d4
--- /dev/null
+++ b/backend/types/deployment.go
@@ -0,0 +1,35 @@
+package types
+
+import (
+	appsv1 "k8s.io/api/apps/v1"
+)
+
+type DeploymentsResponse struct {
+	Success bool                `json:"success"`
+	Msg     string              `json:"msg"`
+	Data    []appsv1.Deployment `json:"data"`
+}
+
+type DeploymentResponse struct {
+	Success bool              `json:"success"`
+	Msg     string            `json:"msg"`
+	Data    appsv1.Deployment `json:"data"`
+}
+
+func NewDeploymentsResponse() DeploymentsResponse {
+	r := DeploymentsResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []appsv1.Deployment{},
+	}
+	return r
+}
+
+func NewDeploymentResponse() DeploymentResponse {
+	r := DeploymentResponse{
+		Success: false,
+		Msg:     "",
+		Data:    appsv1.Deployment{},
+	}
+	return r
+}

diff --git a/backend/types/ingresses.go b/backend/types/ingresses.go
line changes: +13/-0
index 0000000..fe5d20e
--- /dev/null
+++ b/backend/types/ingresses.go
@@ -0,0 +1,13 @@
+package types
+
+import networkingv1 "k8s.io/api/networking/v1"
+
+type IngressesResponse struct {
+	Success bool                   `json:"success"`
+	Msg     string                 `json:"msg"`
+	Data    []networkingv1.Ingress `json:"data"`
+}
+
+func NewIngressesResponse() IngressesResponse {
+	return IngressesResponse{}
+}

diff --git a/backend/types/namespace.go b/backend/types/namespace.go
line changes: +35/-0
index 0000000..16b8650
--- /dev/null
+++ b/backend/types/namespace.go
@@ -0,0 +1,35 @@
+package types
+
+import (
+	corev1 "k8s.io/api/core/v1"
+)
+
+type NamespacesResponse struct {
+	Success bool               `json:"success"`
+	Msg     string             `json:"msg"`
+	Data    []corev1.Namespace `json:"data"`
+}
+
+type NamespaceResponse struct {
+	Success bool             `json:"success"`
+	Msg     string           `json:"msg"`
+	Data    corev1.Namespace `json:"data"`
+}
+
+func NewNamespacesResponse() NamespacesResponse {
+	r := NamespacesResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []corev1.Namespace{},
+	}
+	return r
+}
+
+func NewNamespaceResponse() NamespaceResponse {
+	r := NamespaceResponse{
+		Success: false,
+		Msg:     "",
+		Data:    corev1.Namespace{},
+	}
+	return r
+}

diff --git a/backend/types/persistent_volume_claims.go b/backend/types/persistent_volume_claims.go
line changes: +35/-0
index 0000000..e0ef596
--- /dev/null
+++ b/backend/types/persistent_volume_claims.go
@@ -0,0 +1,35 @@
+package types
+
+import (
+	corev1 "k8s.io/api/core/v1"
+)
+
+type PersistentVolumeClaimsResponse struct {
+	Success bool                           `json:"success"`
+	Msg     string                         `json:"msg"`
+	Data    []corev1.PersistentVolumeClaim `json:"data"`
+}
+
+type PersistentVolumeClaimResponse struct {
+	Success bool                         `json:"success"`
+	Msg     string                       `json:"msg"`
+	Data    corev1.PersistentVolumeClaim `json:"data"`
+}
+
+func NewPersistentVolumeClaimsResponse() PersistentVolumeClaimsResponse {
+	r := PersistentVolumeClaimsResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []corev1.PersistentVolumeClaim{},
+	}
+	return r
+}
+
+func NewPersistentVolumeClaimResponse() PersistentVolumeClaimResponse {
+	r := PersistentVolumeClaimResponse{
+		Success: false,
+		Msg:     "",
+		Data:    corev1.PersistentVolumeClaim{},
+	}
+	return r
+}

diff --git a/backend/types/persistent_volumes.go b/backend/types/persistent_volumes.go
line changes: +35/-0
index 0000000..2989dd9
--- /dev/null
+++ b/backend/types/persistent_volumes.go
@@ -0,0 +1,35 @@
+package types
+
+import (
+	corev1 "k8s.io/api/core/v1"
+)
+
+type PersistentVolumesResponse struct {
+	Success bool                      `json:"success"`
+	Msg     string                    `json:"msg"`
+	Data    []corev1.PersistentVolume `json:"data"`
+}
+
+type PersistentVolumeResponse struct {
+	Success bool                    `json:"success"`
+	Msg     string                  `json:"msg"`
+	Data    corev1.PersistentVolume `json:"data"`
+}
+
+func NewPersistentVolumesResponse() PersistentVolumesResponse {
+	r := PersistentVolumesResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []corev1.PersistentVolume{},
+	}
+	return r
+}
+
+func NewPersistentVolumeResponse() PersistentVolumeResponse {
+	r := PersistentVolumeResponse{
+		Success: false,
+		Msg:     "",
+		Data:    corev1.PersistentVolume{},
+	}
+	return r
+}

diff --git a/backend/types/pod.go b/backend/types/pod.go
line changes: +7/-0
index 0000000..e0e0f0f
--- /dev/null
+++ b/backend/types/pod.go
@@ -0,0 +1,7 @@
+package types
+
+type V1Pod struct {
+	apiVersion string
+	kind       string
+	metadata   map[string]interface{}
+}

diff --git a/backend/types/secrets.go b/backend/types/secrets.go
line changes: +35/-0
index 0000000..89b10ef
--- /dev/null
+++ b/backend/types/secrets.go
@@ -0,0 +1,35 @@
+package types
+
+import (
+	corev1 "k8s.io/api/core/v1"
+)
+
+type SecretsResponse struct {
+	Success bool            `json:"success"`
+	Msg     string          `json:"msg"`
+	Data    []corev1.Secret `json:"data"`
+}
+
+type SecretResponse struct {
+	Success bool          `json:"success"`
+	Msg     string        `json:"msg"`
+	Data    corev1.Secret `json:"data"`
+}
+
+func NewSecretsResponse() SecretsResponse {
+	r := SecretsResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []corev1.Secret{},
+	}
+	return r
+}
+
+func NewSecretResponse() SecretResponse {
+	r := SecretResponse{
+		Success: false,
+		Msg:     "",
+		Data:    corev1.Secret{},
+	}
+	return r
+}

diff --git a/backend/types/statefulset.go b/backend/types/statefulset.go
line changes: +35/-0
index 0000000..32e8392
--- /dev/null
+++ b/backend/types/statefulset.go
@@ -0,0 +1,35 @@
+package types
+
+import (
+	appsv1 "k8s.io/api/apps/v1"
+)
+
+type StatefulSetsResponse struct {
+	Success bool                 `json:"success"`
+	Msg     string               `json:"msg"`
+	Data    []appsv1.StatefulSet `json:"data"`
+}
+
+type StatefulSetResponse struct {
+	Success bool               `json:"success"`
+	Msg     string             `json:"msg"`
+	Data    appsv1.StatefulSet `json:"data"`
+}
+
+func NewStatefulSetsResponse() StatefulSetsResponse {
+	r := StatefulSetsResponse{
+		Success: false,
+		Msg:     "",
+		Data:    []appsv1.StatefulSet{},
+	}
+	return r
+}
+
+func NewStatefulSetResponse() StatefulSetResponse {
+	r := StatefulSetResponse{
+		Success: false,
+		Msg:     "",
+		Data:    appsv1.StatefulSet{},
+	}
+	return r
+}

diff --git a/frontend/README.md b/frontend/README.md
line changes: +8/-0
index 0000000..b4719be
--- /dev/null
+++ b/frontend/README.md
@@ -0,0 +1,8 @@
+# Vue 3 + Vite
+
+This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs,
+check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+
+## Recommended IDE Setup
+
+- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)

diff --git a/frontend/index.html b/frontend/index.html
line changes: +13/-0
index 0000000..52bd276
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8"/>
+    <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
+    <title>kd</title>
+</head>
+<body>
+<div id="app"></div>
+<script src="./src/main.ts" type="module"></script>
+</body>
+</html>
+

diff --git a/frontend/package-lock.json b/frontend/package-lock.json
line changes: +10129/-0
index 0000000..375877e
--- /dev/null
+++ b/frontend/package-lock.json
@@ -0,0 +1,10129 @@
+{
+  "name": "frontend",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "frontend",
+      "version": "0.0.0",
+      "dependencies": {
+        "@xterm/addon-fit": "^0.10.0",
+        "@xterm/addon-web-links": "^0.11.0",
+        "@xterm/xterm": "^5.5.0",
+        "js-yaml": "^4.1.0",
+        "vue": "^3.2.37"
+      },
+      "devDependencies": {
+        "@types/node": "^22.13.8",
+        "@vitejs/plugin-vue": "^3.2.0",
+        "@vue/cli-plugin-typescript": "^5.0.8",
+        "@vue/runtime-core": "^3.5.13",
+        "@vue/tsconfig": "^0.7.0",
+        "typescript": "^5.8.2",
+        "vite": "^3.0.7"
+      }
+    },
+    "node_modules/@achrinza/node-ipc": {
+      "version": "9.2.9",
+      "resolved": "https://registry.npmjs.org/@achrinza/node-ipc/-/node-ipc-9.2.9.tgz",
+      "integrity": "sha512-7s0VcTwiK/0tNOVdSX9FWMeFdOEcsAOz9HesBldXxFMaGvIak7KC2z9tV9EgsQXn6KUsWsfIkViMNuIo0GoZDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@node-ipc/js-queue": "2.0.3",
+        "event-pubsub": "4.3.0",
+        "js-message": "1.0.7"
+      },
+      "engines": {
+        "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22"
+      }
+    },
+    "node_modules/@ampproject/remapping": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.26.2",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+      "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.25.9",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.26.8",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
+      "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz",
+      "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.26.2",
+        "@babel/generator": "^7.26.9",
+        "@babel/helper-compilation-targets": "^7.26.5",
+        "@babel/helper-module-transforms": "^7.26.0",
+        "@babel/helpers": "^7.26.9",
+        "@babel/parser": "^7.26.9",
+        "@babel/template": "^7.26.9",
+        "@babel/traverse": "^7.26.9",
+        "@babel/types": "^7.26.9",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz",
+      "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.26.9",
+        "@babel/types": "^7.26.9",
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.26.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz",
+      "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/compat-data": "^7.26.5",
+        "@babel/helper-validator-option": "^7.25.9",
+        "browserslist": "^4.24.0",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
+      "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/traverse": "^7.25.9",
+        "@babel/types": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.26.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
+      "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.25.9",
+        "@babel/helper-validator-identifier": "^7.25.9",
+        "@babel/traverse": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+      "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+      "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
+      "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz",
+      "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/template": "^7.26.9",
+        "@babel/types": "^7.26.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
+      "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.26.9"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
+      "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.26.2",
+        "@babel/parser": "^7.26.9",
+        "@babel/types": "^7.26.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz",
+      "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.26.2",
+        "@babel/generator": "^7.26.9",
+        "@babel/parser": "^7.26.9",
+        "@babel/template": "^7.26.9",
+        "@babel/types": "^7.26.9",
+        "debug": "^4.3.1",
+        "globals": "^11.1.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
+      "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.25.9",
+        "@babel/helper-validator-identifier": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@discoveryjs/json-ext": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+      "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
+      "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz",
+      "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@hapi/hoek": {
+      "version": "9.3.0",
+      "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
+      "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@hapi/topo": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
+      "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@hapi/hoek": "^9.0.0"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+      "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+      "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/source-map": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+      "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/@leichtgewicht/ip-codec": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
+      "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@node-ipc/js-queue": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@node-ipc/js-queue/-/js-queue-2.0.3.tgz",
+      "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "easy-stack": "1.0.1"
+      },
+      "engines": {
+        "node": ">=1.0.0"
+      }
+    },
+    "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/@polka/url": {
+      "version": "1.0.0-next.28",
+      "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz",
+      "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@sideway/address": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
+      "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@hapi/hoek": "^9.0.0"
+      }
+    },
+    "node_modules/@sideway/formula": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
+      "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@sideway/pinpoint": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
+      "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@soda/friendly-errors-webpack-plugin": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
+      "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "chalk": "^3.0.0",
+        "error-stack-parser": "^2.0.6",
+        "string-width": "^4.2.3",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      },
+      "peerDependencies": {
+        "webpack": "^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/@soda/get-current-script": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@soda/get-current-script/-/get-current-script-1.0.2.tgz",
+      "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@trysound/sax": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
+      "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.5",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
+      "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/bonjour": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz",
+      "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.38",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect-history-api-fallback": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz",
+      "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/express-serve-static-core": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/eslint": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+      "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "*",
+        "@types/json-schema": "*"
+      }
+    },
+    "node_modules/@types/eslint-scope": {
+      "version": "3.7.7",
+      "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+      "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/eslint": "*",
+        "@types/estree": "*"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/express": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
+      "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^4.17.33",
+        "@types/qs": "*",
+        "@types/serve-static": "*"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
+      "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/express/node_modules/@types/express-serve-static-core": {
+      "version": "4.19.6",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
+      "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/html-minifier-terser": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+      "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
+      "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@types/http-proxy": {
+      "version": "1.17.16",
+      "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz",
+      "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/mime": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
+      "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@types/minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@types/node": {
+      "version": "22.13.8",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz",
+      "integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.20.0"
+      }
+    },
+    "node_modules/@types/node-forge": {
+      "version": "1.3.11",
+      "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz",
+      "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/normalize-package-data": {
+      "version": "2.4.4",
+      "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
+      "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/parse-json": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+      "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/qs": {
+      "version": "6.9.18",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
+      "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@types/retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@types/send": {
+      "version": "0.17.4",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
+      "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/mime": "^1",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-index": {
+      "version": "1.9.4",
+      "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz",
+      "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "1.15.7",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
+      "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/sockjs": {
+      "version": "0.3.36",
+      "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz",
+      "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/webpack-env": {
+      "version": "1.18.8",
+      "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.8.tgz",
+      "integrity": "sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/ws": {
+      "version": "8.5.14",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz",
+      "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz",
+      "integrity": "sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^3.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/cli-overlay": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-5.0.8.tgz",
+      "integrity": "sha512-KmtievE/B4kcXp6SuM2gzsnSd8WebkQpg3XaB6GmFh1BJGRqa1UiW9up7L/Q67uOdTigHxr5Ar2lZms4RcDjwQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@vue/cli-plugin-router": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-5.0.8.tgz",
+      "integrity": "sha512-Gmv4dsGdAsWPqVijz3Ux2OS2HkMrWi1ENj2cYL75nUeL+Xj5HEstSqdtfZ0b1q9NCce+BFB6QnHfTBXc/fCvMg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@vue/cli-shared-utils": "^5.0.8"
+      },
+      "peerDependencies": {
+        "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0"
+      }
+    },
+    "node_modules/@vue/cli-plugin-typescript": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-typescript/-/cli-plugin-typescript-5.0.8.tgz",
+      "integrity": "sha512-JKJOwzJshBqsmp4yLBexwVMebOZ4VGJgbnYvmHVxasJOStF2RxwyW28ZF+zIvASGdat4sAUuo/3mAQyVhm7JHg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/core": "^7.12.16",
+        "@types/webpack-env": "^1.15.2",
+        "@vue/cli-shared-utils": "^5.0.8",
+        "babel-loader": "^8.2.2",
+        "fork-ts-checker-webpack-plugin": "^6.4.0",
+        "globby": "^11.0.2",
+        "thread-loader": "^3.0.0",
+        "ts-loader": "^9.2.5",
+        "webpack": "^5.54.0"
+      },
+      "peerDependencies": {
+        "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0",
+        "cache-loader": "^4.1.0",
+        "typescript": ">=2",
+        "vue": "^2 || ^3.2.13",
+        "vue-template-compiler": "^2.0.0"
+      },
+      "peerDependenciesMeta": {
+        "cache-loader": {
+          "optional": true
+        },
+        "vue-template-compiler": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/cli-plugin-vuex": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.8.tgz",
+      "integrity": "sha512-HSYWPqrunRE5ZZs8kVwiY6oWcn95qf/OQabwLfprhdpFWAGtLStShjsGED2aDpSSeGAskQETrtR/5h7VqgIlBA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "peerDependencies": {
+        "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0"
+      }
+    },
+    "node_modules/@vue/cli-service": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-5.0.8.tgz",
+      "integrity": "sha512-nV7tYQLe7YsTtzFrfOMIHc5N2hp5lHG2rpYr0aNja9rNljdgcPZLyQRb2YRivTHqTv7lI962UXFURcpStHgyFw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@babel/helper-compilation-targets": "^7.12.16",
+        "@soda/friendly-errors-webpack-plugin": "^1.8.0",
+        "@soda/get-current-script": "^1.0.2",
+        "@types/minimist": "^1.2.0",
+        "@vue/cli-overlay": "^5.0.8",
+        "@vue/cli-plugin-router": "^5.0.8",
+        "@vue/cli-plugin-vuex": "^5.0.8",
+        "@vue/cli-shared-utils": "^5.0.8",
+        "@vue/component-compiler-utils": "^3.3.0",
+        "@vue/vue-loader-v15": "npm:vue-loader@^15.9.7",
+        "@vue/web-component-wrapper": "^1.3.0",
+        "acorn": "^8.0.5",
+        "acorn-walk": "^8.0.2",
+        "address": "^1.1.2",
+        "autoprefixer": "^10.2.4",
+        "browserslist": "^4.16.3",
+        "case-sensitive-paths-webpack-plugin": "^2.3.0",
+        "cli-highlight": "^2.1.10",
+        "clipboardy": "^2.3.0",
+        "cliui": "^7.0.4",
+        "copy-webpack-plugin": "^9.0.1",
+        "css-loader": "^6.5.0",
+        "css-minimizer-webpack-plugin": "^3.0.2",
+        "cssnano": "^5.0.0",
+        "debug": "^4.1.1",
+        "default-gateway": "^6.0.3",
+        "dotenv": "^10.0.0",
+        "dotenv-expand": "^5.1.0",
+        "fs-extra": "^9.1.0",
+        "globby": "^11.0.2",
+        "hash-sum": "^2.0.0",
+        "html-webpack-plugin": "^5.1.0",
+        "is-file-esm": "^1.0.0",
+        "launch-editor-middleware": "^2.2.1",
+        "lodash.defaultsdeep": "^4.6.1",
+        "lodash.mapvalues": "^4.6.0",
+        "mini-css-extract-plugin": "^2.5.3",
+        "minimist": "^1.2.5",
+        "module-alias": "^2.2.2",
+        "portfinder": "^1.0.26",
+        "postcss": "^8.2.6",
+        "postcss-loader": "^6.1.1",
+        "progress-webpack-plugin": "^1.0.12",
+        "ssri": "^8.0.1",
+        "terser-webpack-plugin": "^5.1.1",
+        "thread-loader": "^3.0.0",
+        "vue-loader": "^17.0.0",
+        "vue-style-loader": "^4.1.3",
+        "webpack": "^5.54.0",
+        "webpack-bundle-analyzer": "^4.4.0",
+        "webpack-chain": "^6.5.1",
+        "webpack-dev-server": "^4.7.3",
+        "webpack-merge": "^5.7.3",
+        "webpack-virtual-modules": "^0.4.2",
+        "whatwg-fetch": "^3.6.2"
+      },
+      "bin": {
+        "vue-cli-service": "bin/vue-cli-service.js"
+      },
+      "engines": {
+        "node": "^12.0.0 || >= 14.0.0"
+      },
+      "peerDependencies": {
+        "vue-template-compiler": "^2.0.0",
+        "webpack-sources": "*"
+      },
+      "peerDependenciesMeta": {
+        "cache-loader": {
+          "optional": true
+        },
+        "less-loader": {
+          "optional": true
+        },
+        "pug-plain-loader": {
+          "optional": true
+        },
+        "raw-loader": {
+          "optional": true
+        },
+        "sass-loader": {
+          "optional": true
+        },
+        "stylus-loader": {
+          "optional": true
+        },
+        "vue-template-compiler": {
+          "optional": true
+        },
+        "webpack-sources": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/cli-shared-utils": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz",
+      "integrity": "sha512-uK2YB7bBVuQhjOJF+O52P9yFMXeJVj7ozqJkwYE9PlMHL1LMHjtCYm4cSdOebuPzyP+/9p0BimM/OqxsevIopQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@achrinza/node-ipc": "^9.2.5",
+        "chalk": "^4.1.2",
+        "execa": "^1.0.0",
+        "joi": "^17.4.0",
+        "launch-editor": "^2.2.1",
+        "lru-cache": "^6.0.0",
+        "node-fetch": "^2.6.7",
+        "open": "^8.0.2",
+        "ora": "^5.3.0",
+        "read-pkg": "^5.1.1",
+        "semver": "^7.3.4",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "node_modules/@vue/cli-shared-utils/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/@vue/cli-shared-utils/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@vue/cli-shared-utils/node_modules/semver": {
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+      "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@vue/cli-shared-utils/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
+      "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.25.3",
+        "@vue/shared": "3.5.13",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/compiler-core/node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
+      "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.13",
+        "@vue/shared": "3.5.13"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
+      "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.25.3",
+        "@vue/compiler-core": "3.5.13",
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/compiler-ssr": "3.5.13",
+        "@vue/shared": "3.5.13",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.11",
+        "postcss": "^8.4.48",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
+      "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/shared": "3.5.13"
+      }
+    },
+    "node_modules/@vue/component-compiler-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz",
+      "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "consolidate": "^0.15.1",
+        "hash-sum": "^1.0.2",
+        "lru-cache": "^4.1.2",
+        "merge-source-map": "^1.1.0",
+        "postcss": "^7.0.36",
+        "postcss-selector-parser": "^6.0.2",
+        "source-map": "~0.6.1",
+        "vue-template-es2015-compiler": "^1.9.0"
+      },
+      "optionalDependencies": {
+        "prettier": "^1.18.2 || ^2.0.0"
+      }
+    },
+    "node_modules/@vue/component-compiler-utils/node_modules/hash-sum": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
+      "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "pseudomap": "^1.0.2",
+        "yallist": "^2.1.2"
+      }
+    },
+    "node_modules/@vue/component-compiler-utils/node_modules/picocolors": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+      "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/@vue/component-compiler-utils/node_modules/postcss": {
+      "version": "7.0.39",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+      "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "picocolors": "^0.2.1",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/postcss/"
+      }
+    },
+    "node_modules/@vue/component-compiler-utils/node_modules/yallist": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+      "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
+      "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/shared": "3.5.13"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
+      "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.13",
+        "@vue/shared": "3.5.13"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
+      "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.13",
+        "@vue/runtime-core": "3.5.13",
+        "@vue/shared": "3.5.13",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
+      "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.13",
+        "@vue/shared": "3.5.13"
+      },
+      "peerDependencies": {
+        "vue": "3.5.13"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
+      "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
+      "license": "MIT"
+    },
+    "node_modules/@vue/tsconfig": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz",
+      "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "typescript": "5.x",
+        "vue": "^3.4.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        },
+        "vue": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/vue-loader-v15": {
+      "name": "vue-loader",
+      "version": "15.11.1",
+      "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.11.1.tgz",
+      "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@vue/component-compiler-utils": "^3.1.0",
+        "hash-sum": "^1.0.2",
+        "loader-utils": "^1.1.0",
+        "vue-hot-reload-api": "^2.3.0",
+        "vue-style-loader": "^4.1.0"
+      },
+      "peerDependencies": {
+        "css-loader": "*",
+        "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0"
+      },
+      "peerDependenciesMeta": {
+        "cache-loader": {
+          "optional": true
+        },
+        "prettier": {
+          "optional": true
+        },
+        "vue-template-compiler": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/vue-loader-v15/node_modules/hash-sum": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
+      "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@vue/web-component-wrapper": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz",
+      "integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/ast": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+      "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/helper-numbers": "1.13.2",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+      "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-api-error": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+      "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-buffer": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+      "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-numbers": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+      "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+        "@webassemblyjs/helper-api-error": "1.13.2",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+      "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-wasm-section": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+      "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
+      "dev": true,
+      "license": "MIT",
+      "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"
+      }
+    },
+    "node_modules/@webassemblyjs/ieee754": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+      "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "node_modules/@webassemblyjs/leb128": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+      "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/utf8": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+      "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/wasm-edit": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+      "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
+      "dev": true,
+      "license": "MIT",
+      "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"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-gen": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+      "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
+      "dev": true,
+      "license": "MIT",
+      "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"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-opt": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+      "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/wasm-gen": "1.14.1",
+        "@webassemblyjs/wasm-parser": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-parser": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+      "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
+      "dev": true,
+      "license": "MIT",
+      "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"
+      }
+    },
+    "node_modules/@webassemblyjs/wast-printer": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+      "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@xterm/addon-fit": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
+      "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@xterm/xterm": "^5.0.0"
+      }
+    },
+    "node_modules/@xterm/addon-web-links": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.11.0.tgz",
+      "integrity": "sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@xterm/xterm": "^5.0.0"
+      }
+    },
+    "node_modules/@xterm/xterm": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
+      "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
+      "license": "MIT"
+    },
+    "node_modules/@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/accepts/node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.14.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+      "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-walk": {
+      "version": "8.3.4",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+      "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "acorn": "^8.11.0"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/address": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz",
+      "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 10.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/ajv-formats": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ajv-formats/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-formats/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "ajv": "^6.9.1"
+      }
+    },
+    "node_modules/ansi-escapes": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+      "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/ansi-html-community": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+      "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
+      "dev": true,
+      "engines": [
+        "node >= 0.8.0"
+      ],
+      "license": "Apache-2.0",
+      "peer": true,
+      "bin": {
+        "ansi-html": "bin/ansi-html"
+      }
+    },
+    "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",
+      "peer": true
+    },
+    "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/arch": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+      "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
+      "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",
+      "peer": true
+    },
+    "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==",
+      "license": "Python-2.0"
+    },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/async": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+      "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "lodash": "^4.17.14"
+      }
+    },
+    "node_modules/at-least-node": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/autoprefixer": {
+      "version": "10.4.20",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
+      "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.23.3",
+        "caniuse-lite": "^1.0.30001646",
+        "fraction.js": "^4.3.7",
+        "normalize-range": "^0.1.2",
+        "picocolors": "^1.0.1",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "bin": {
+        "autoprefixer": "bin/autoprefixer"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/babel-loader": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz",
+      "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "find-cache-dir": "^3.3.1",
+        "loader-utils": "^2.0.4",
+        "make-dir": "^3.1.0",
+        "schema-utils": "^2.6.5"
+      },
+      "engines": {
+        "node": ">= 8.9"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0",
+        "webpack": ">=2"
+      }
+    },
+    "node_modules/babel-loader/node_modules/loader-utils": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
+    "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/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/batch": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+      "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/big.js": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "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/bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
+    "node_modules/bluebird": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.3",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+      "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.5",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.13.0",
+        "raw-body": "2.5.2",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/body-parser/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/body-parser/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/bonjour-service": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz",
+      "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "multicast-dns": "^7.2.5"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "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/browserslist": {
+      "version": "4.24.4",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+      "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "caniuse-lite": "^1.0.30001688",
+        "electron-to-chromium": "^1.5.73",
+        "node-releases": "^2.0.19",
+        "update-browserslist-db": "^1.1.1"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
+      "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "get-intrinsic": "^1.2.6"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/camel-case": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+      "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "pascal-case": "^3.1.2",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/caniuse-api": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
+      "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.0.0",
+        "caniuse-lite": "^1.0.0",
+        "lodash.memoize": "^4.1.2",
+        "lodash.uniq": "^4.5.0"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001701",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz",
+      "integrity": "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "CC-BY-4.0"
+    },
+    "node_modules/case-sensitive-paths-webpack-plugin": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
+      "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+      "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "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/chrome-trace-event": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
+      "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/clean-css": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
+      "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "source-map": "~0.6.0"
+      },
+      "engines": {
+        "node": ">= 10.0"
+      }
+    },
+    "node_modules/cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "restore-cursor": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cli-highlight": {
+      "version": "2.1.11",
+      "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
+      "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "chalk": "^4.0.0",
+        "highlight.js": "^10.7.1",
+        "mz": "^2.4.0",
+        "parse5": "^5.1.1",
+        "parse5-htmlparser2-tree-adapter": "^6.0.0",
+        "yargs": "^16.0.0"
+      },
+      "bin": {
+        "highlight": "bin/highlight"
+      },
+      "engines": {
+        "node": ">=8.0.0",
+        "npm": ">=5.0.0"
+      }
+    },
+    "node_modules/cli-highlight/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",
+      "peer": true,
+      "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/cli-spinners": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+      "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/clipboardy": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
+      "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "arch": "^2.1.1",
+        "execa": "^1.0.0",
+        "is-wsl": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/clone": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/clone-deep": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "is-plain-object": "^2.0.4",
+        "kind-of": "^6.0.2",
+        "shallow-clone": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/colord": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+      "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/commander": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/commondir": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+      "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "mime-db": ">= 1.43.0 < 2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/compression": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
+      "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "bytes": "3.1.2",
+        "compressible": "~2.0.18",
+        "debug": "2.6.9",
+        "negotiator": "~0.6.4",
+        "on-headers": "~1.0.2",
+        "safe-buffer": "5.2.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/compression/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/compression/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "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/connect-history-api-fallback": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
+      "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/consolidate": {
+      "version": "0.15.1",
+      "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz",
+      "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==",
+      "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "bluebird": "^3.1.1"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cookie": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+      "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/copy-webpack-plugin": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz",
+      "integrity": "sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-glob": "^3.2.7",
+        "glob-parent": "^6.0.1",
+        "globby": "^11.0.3",
+        "normalize-path": "^3.0.0",
+        "schema-utils": "^3.1.1",
+        "serialize-javascript": "^6.0.0"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.1.0"
+      }
+    },
+    "node_modules/copy-webpack-plugin/node_modules/schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/cosmiconfig": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+      "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.1.0",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.7.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "6.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
+      "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      },
+      "engines": {
+        "node": ">=4.8"
+      }
+    },
+    "node_modules/cross-spawn/node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/css-declaration-sorter": {
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz",
+      "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.0.9"
+      }
+    },
+    "node_modules/css-loader": {
+      "version": "6.11.0",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz",
+      "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "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"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "@rspack/core": "0.x || 1.x",
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@rspack/core": {
+          "optional": true
+        },
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/css-loader/node_modules/semver": {
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+      "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/css-minimizer-webpack-plugin": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz",
+      "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cssnano": "^5.0.6",
+        "jest-worker": "^27.0.2",
+        "postcss": "^8.3.5",
+        "schema-utils": "^4.0.0",
+        "serialize-javascript": "^6.0.0",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@parcel/css": {
+          "optional": true
+        },
+        "clean-css": {
+          "optional": true
+        },
+        "csso": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
+      "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/css-select": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.0.1",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-tree": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+      "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "mdn-data": "2.0.14",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "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",
+      "peer": true,
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/cssnano": {
+      "version": "5.1.15",
+      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz",
+      "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cssnano-preset-default": "^5.2.14",
+        "lilconfig": "^2.0.3",
+        "yaml": "^1.10.2"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/cssnano"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/cssnano-preset-default": {
+      "version": "5.2.14",
+      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz",
+      "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "css-declaration-sorter": "^6.3.1",
+        "cssnano-utils": "^3.1.0",
+        "postcss-calc": "^8.2.3",
+        "postcss-colormin": "^5.3.1",
+        "postcss-convert-values": "^5.1.3",
+        "postcss-discard-comments": "^5.1.2",
+        "postcss-discard-duplicates": "^5.1.0",
+        "postcss-discard-empty": "^5.1.1",
+        "postcss-discard-overridden": "^5.1.0",
+        "postcss-merge-longhand": "^5.1.7",
+        "postcss-merge-rules": "^5.1.4",
+        "postcss-minify-font-values": "^5.1.0",
+        "postcss-minify-gradients": "^5.1.1",
+        "postcss-minify-params": "^5.1.4",
+        "postcss-minify-selectors": "^5.2.1",
+        "postcss-normalize-charset": "^5.1.0",
+        "postcss-normalize-display-values": "^5.1.0",
+        "postcss-normalize-positions": "^5.1.1",
+        "postcss-normalize-repeat-style": "^5.1.1",
+        "postcss-normalize-string": "^5.1.0",
+        "postcss-normalize-timing-functions": "^5.1.0",
+        "postcss-normalize-unicode": "^5.1.1",
+        "postcss-normalize-url": "^5.1.0",
+        "postcss-normalize-whitespace": "^5.1.1",
+        "postcss-ordered-values": "^5.1.3",
+        "postcss-reduce-initial": "^5.1.2",
+        "postcss-reduce-transforms": "^5.1.0",
+        "postcss-svgo": "^5.1.0",
+        "postcss-unique-selectors": "^5.1.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/cssnano-utils": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz",
+      "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/csso": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
+      "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "css-tree": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "license": "MIT"
+    },
+    "node_modules/debounce": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
+      "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/debug": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+      "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deepmerge": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/default-gateway": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz",
+      "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "dependencies": {
+        "execa": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/default-gateway/node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/default-gateway/node_modules/execa": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.0",
+        "human-signals": "^2.1.0",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.1",
+        "onetime": "^5.1.2",
+        "signal-exit": "^3.0.3",
+        "strip-final-newline": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/default-gateway/node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-gateway/node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-gateway/node_modules/npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "path-key": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/default-gateway/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",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/default-gateway/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",
+      "peer": true,
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/default-gateway/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",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/default-gateway/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",
+      "peer": true,
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/defaults": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+      "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/define-lazy-prop": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+      "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/detect-node": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/dns-packet": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
+      "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@leichtgewicht/ip-codec": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/dom-converter": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+      "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "utila": "~0.4"
+      }
+    },
+    "node_modules/dom-serializer": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "license": "BSD-2-Clause",
+      "peer": true
+    },
+    "node_modules/domhandler": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "dependencies": {
+        "domelementtype": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "dependencies": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/dot-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+      "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+      "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/duplexer": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+      "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/easy-stack": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz",
+      "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.109",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.109.tgz",
+      "integrity": "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "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",
+      "peer": true
+    },
+    "node_modules/emojis-list": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+      "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/enhanced-resolve": {
+      "version": "5.18.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
+      "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/enhanced-resolve/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/entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/error-stack-parser": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
+      "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "stackframe": "^1.3.4"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "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",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
+      "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
+      "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.15.18",
+        "@esbuild/linux-loong64": "0.15.18",
+        "esbuild-android-64": "0.15.18",
+        "esbuild-android-arm64": "0.15.18",
+        "esbuild-darwin-64": "0.15.18",
+        "esbuild-darwin-arm64": "0.15.18",
+        "esbuild-freebsd-64": "0.15.18",
+        "esbuild-freebsd-arm64": "0.15.18",
+        "esbuild-linux-32": "0.15.18",
+        "esbuild-linux-64": "0.15.18",
+        "esbuild-linux-arm": "0.15.18",
+        "esbuild-linux-arm64": "0.15.18",
+        "esbuild-linux-mips64le": "0.15.18",
+        "esbuild-linux-ppc64le": "0.15.18",
+        "esbuild-linux-riscv64": "0.15.18",
+        "esbuild-linux-s390x": "0.15.18",
+        "esbuild-netbsd-64": "0.15.18",
+        "esbuild-openbsd-64": "0.15.18",
+        "esbuild-sunos-64": "0.15.18",
+        "esbuild-windows-32": "0.15.18",
+        "esbuild-windows-64": "0.15.18",
+        "esbuild-windows-arm64": "0.15.18"
+      }
+    },
+    "node_modules/esbuild-android-64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz",
+      "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-android-arm64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz",
+      "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-darwin-64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz",
+      "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-darwin-arm64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz",
+      "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-freebsd-64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz",
+      "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-freebsd-arm64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz",
+      "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-32": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz",
+      "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz",
+      "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-arm": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz",
+      "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-arm64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz",
+      "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-mips64le": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz",
+      "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-ppc64le": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz",
+      "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-riscv64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz",
+      "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-s390x": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz",
+      "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-netbsd-64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz",
+      "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-openbsd-64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz",
+      "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-sunos-64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz",
+      "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-32": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz",
+      "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz",
+      "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-arm64": {
+      "version": "0.15.18",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz",
+      "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "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/esrecurse/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/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "license": "MIT"
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/event-pubsub": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz",
+      "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==",
+      "dev": true,
+      "license": "Unlicense",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/execa": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cross-spawn": "^6.0.0",
+        "get-stream": "^4.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/express": {
+      "version": "4.21.2",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+      "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.20.3",
+        "content-disposition": "0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "0.7.1",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.3.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "merge-descriptors": "1.0.3",
+        "methods": "~1.1.2",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.12",
+        "proxy-addr": "~2.0.7",
+        "qs": "6.13.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "0.19.0",
+        "serve-static": "1.16.2",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/express/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/express/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "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.3",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.8"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-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-uri": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+      "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/fastq": {
+      "version": "1.19.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+      "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/faye-websocket": {
+      "version": "0.11.4",
+      "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+      "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "dependencies": {
+        "websocket-driver": ">=0.5.1"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/figures": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+      "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "escape-string-regexp": "^1.0.5"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "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/finalhandler": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
+      "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "2.0.1",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/finalhandler/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/finalhandler/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/find-cache-dir": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
+      "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "commondir": "^1.0.1",
+        "make-dir": "^3.0.2",
+        "pkg-dir": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "peer": true,
+      "bin": {
+        "flat": "cli.js"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin": {
+      "version": "6.5.3",
+      "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz",
+      "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.8.3",
+        "@types/json-schema": "^7.0.5",
+        "chalk": "^4.1.0",
+        "chokidar": "^3.4.2",
+        "cosmiconfig": "^6.0.0",
+        "deepmerge": "^4.2.2",
+        "fs-extra": "^9.0.0",
+        "glob": "^7.1.6",
+        "memfs": "^3.1.2",
+        "minimatch": "^3.0.4",
+        "schema-utils": "2.7.0",
+        "semver": "^7.3.2",
+        "tapable": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=10",
+        "yarn": ">=1.0.0"
+      },
+      "peerDependencies": {
+        "eslint": ">= 6",
+        "typescript": ">= 2.7",
+        "vue-template-compiler": "*",
+        "webpack": ">= 4"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        },
+        "vue-template-compiler": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin/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/fork-ts-checker-webpack-plugin/node_modules/schema-utils": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+      "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.4",
+        "ajv": "^6.12.2",
+        "ajv-keywords": "^3.4.1"
+      },
+      "engines": {
+        "node": ">= 8.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": {
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+      "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fraction.js": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+      "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "type": "patreon",
+        "url": "https://github.com/sponsors/rawify"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fs-extra": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+      "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "at-least-node": "^1.0.0",
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/fs-monkey": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz",
+      "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==",
+      "dev": true,
+      "license": "Unlicense"
+    },
+    "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/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "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",
+      "peer": true,
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "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/gzip-size": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
+      "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "duplexer": "^0.1.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/handle-thing": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+      "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hash-sum": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
+      "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "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/he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "he": "bin/he"
+      }
+    },
+    "node_modules/highlight.js": {
+      "version": "10.7.3",
+      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+      "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "peer": true,
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/hosted-git-info": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/hpack.js": {
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+      "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "inherits": "^2.0.1",
+        "obuf": "^1.0.0",
+        "readable-stream": "^2.0.1",
+        "wbuf": "^1.1.0"
+      }
+    },
+    "node_modules/hpack.js/node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/hpack.js/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/hpack.js/node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/html-entities": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz",
+      "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/mdevils"
+        },
+        {
+          "type": "patreon",
+          "url": "https://patreon.com/mdevils"
+        }
+      ],
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/html-minifier-terser": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+      "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "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"
+      },
+      "bin": {
+        "html-minifier-terser": "cli.js"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/html-webpack-plugin": {
+      "version": "5.6.3",
+      "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz",
+      "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "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"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/html-webpack-plugin"
+      },
+      "peerDependencies": {
+        "@rspack/core": "0.x || 1.x",
+        "webpack": "^5.20.0"
+      },
+      "peerDependenciesMeta": {
+        "@rspack/core": {
+          "optional": true
+        },
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/html-webpack-plugin/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",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/htmlparser2": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+      "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+      "dev": true,
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.0.0",
+        "domutils": "^2.5.2",
+        "entities": "^2.0.0"
+      }
+    },
+    "node_modules/http-deceiver": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+      "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/http-parser-js": {
+      "version": "0.5.9",
+      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz",
+      "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/http-proxy": {
+      "version": "1.18.1",
+      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "eventemitter3": "^4.0.0",
+        "follow-redirects": "^1.0.0",
+        "requires-port": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/http-proxy-middleware": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz",
+      "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/http-proxy": "^1.17.8",
+        "http-proxy": "^1.18.1",
+        "is-glob": "^4.0.1",
+        "is-plain-obj": "^3.0.0",
+        "micromatch": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "@types/express": "^4.17.13"
+      },
+      "peerDependenciesMeta": {
+        "@types/express": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/human-signals": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "engines": {
+        "node": ">=10.17.0"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/icss-utils": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+      "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/ignore": {
+      "version": "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.1",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/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/ipaddr.js": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
+      "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "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-core-module": {
+      "version": "2.16.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+      "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-docker": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+      "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/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-file-esm": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-file-esm/-/is-file-esm-1.0.0.tgz",
+      "integrity": "sha512-rZlaNKb4Mr8WlRu2A9XdeoKgnO5aA53XdPHgCKVyCrQ/rWi89RET1+bq37Ru46obaQXeiX4vmFIm1vks41hoSA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "read-pkg-up": "^7.0.1"
+      }
+    },
+    "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",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-interactive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-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-plain-obj": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
+      "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-stream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+      "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-wsl": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-docker": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "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/isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/javascript-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz",
+      "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/jest-worker": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      }
+    },
+    "node_modules/jest-worker/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/joi": {
+      "version": "17.13.3",
+      "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
+      "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@hapi/hoek": "^9.3.0",
+        "@hapi/topo": "^5.1.0",
+        "@sideway/address": "^4.1.5",
+        "@sideway/formula": "^3.0.1",
+        "@sideway/pinpoint": "^2.0.0"
+      }
+    },
+    "node_modules/js-message": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz",
+      "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6.0"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/jsesc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "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/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/klona": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
+      "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/launch-editor": {
+      "version": "2.10.0",
+      "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz",
+      "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "picocolors": "^1.0.0",
+        "shell-quote": "^1.8.1"
+      }
+    },
+    "node_modules/launch-editor-middleware": {
+      "version": "2.10.0",
+      "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.10.0.tgz",
+      "integrity": "sha512-RzZu7MeVlE3p1H6Sadc2BhuDGAj7bkeDCBpNq/zSENP4ohJGhso00k5+iYaRwKshIpiOAhMmimce+5D389xmSg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "launch-editor": "^2.10.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",
+      "peer": true,
+      "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/loader-runner": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+      "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.11.5"
+      }
+    },
+    "node_modules/loader-utils": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
+      "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/loader-utils/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",
+      "peer": true,
+      "dependencies": {
+        "minimist": "^1.2.0"
+      },
+      "bin": {
+        "json5": "lib/cli.js"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash.defaultsdeep": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
+      "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash.mapvalues": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
+      "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash.memoize": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-symbols/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/log-update": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz",
+      "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-escapes": "^3.0.0",
+        "cli-cursor": "^2.0.0",
+        "wrap-ansi": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/ansi-regex": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+      "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/cli-cursor": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+      "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "restore-cursor": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/mimic-fn": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/onetime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+      "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "mimic-fn": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/restore-cursor": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+      "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "onetime": "^2.0.0",
+        "signal-exit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/string-width": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/strip-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+      "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/log-update/node_modules/wrap-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz",
+      "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "string-width": "^2.1.1",
+        "strip-ansi": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/lower-case": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.17",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mdn-data": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
+      "dev": true,
+      "license": "CC0-1.0",
+      "peer": true
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/memfs": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
+      "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
+      "dev": true,
+      "license": "Unlicense",
+      "dependencies": {
+        "fs-monkey": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/merge-source-map": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
+      "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "source-map": "^0.6.1"
+      }
+    },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "braces": "^3.0.3",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/mini-css-extract-plugin": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz",
+      "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "schema-utils": "^4.0.0",
+        "tapable": "^2.2.1"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.0.0"
+      }
+    },
+    "node_modules/mini-css-extract-plugin/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
+      "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/mini-css-extract-plugin/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",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "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",
+      "peer": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/module-alias": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz",
+      "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/mrmime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+      "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "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/multicast-dns": {
+      "version": "7.2.5",
+      "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
+      "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "dns-packet": "^5.2.2",
+        "thunky": "^1.0.2"
+      },
+      "bin": {
+        "multicast-dns": "cli.js"
+      }
+    },
+    "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",
+      "peer": true,
+      "dependencies": {
+        "any-promise": "^1.0.0",
+        "object-assign": "^4.0.1",
+        "thenify-all": "^1.0.0"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+      "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+      "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/negotiator": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
+      "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/no-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "lower-case": "^2.0.2",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/node-fetch": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/node-forge": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
+      "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+      "dev": true,
+      "license": "(BSD-3-Clause OR GPL-2.0)",
+      "peer": true,
+      "engines": {
+        "node": ">= 6.13.0"
+      }
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.19",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+      "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "node_modules/normalize-package-data/node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/normalize-range": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+      "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/normalize-url": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/npm-run-path": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+      "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/obuf": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+      "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/on-headers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "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/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/open": {
+      "version": "8.4.2",
+      "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
+      "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "define-lazy-prop": "^2.0.0",
+        "is-docker": "^2.1.1",
+        "is-wsl": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/opener": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
+      "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+      "dev": true,
+      "license": "(WTFPL OR MIT)",
+      "peer": true,
+      "bin": {
+        "opener": "bin/opener-bin.js"
+      }
+    },
+    "node_modules/ora": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bl": "^4.1.0",
+        "chalk": "^4.1.0",
+        "cli-cursor": "^3.1.0",
+        "cli-spinners": "^2.5.0",
+        "is-interactive": "^1.0.0",
+        "is-unicode-supported": "^0.1.0",
+        "log-symbols": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "wcwidth": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ora/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/p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/p-retry": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+      "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/retry": "0.12.0",
+        "retry": "^0.13.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/param-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+      "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "dot-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dev": true,
+      "license": "MIT",
+      "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"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+      "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/parse5-htmlparser2-tree-adapter": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+      "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "parse5": "^6.0.1"
+      }
+    },
+    "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+      "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/pascal-case": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-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": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "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-to-regexp": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+      "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "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/pkg-dir": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "find-up": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/portfinder": {
+      "version": "1.0.33",
+      "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.33.tgz",
+      "integrity": "sha512-+2jndHT63cL5MdQOwDm9OT2dIe11zVpjV+0GGRXdtO1wpPxv260NfVqoEXtYAi/shanmm3W4+yLduIe55ektTw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "async": "^2.6.4",
+        "debug": "^3.2.7",
+        "mkdirp": "^0.5.6"
+      },
+      "engines": {
+        "node": ">= 0.12.0"
+      }
+    },
+    "node_modules/portfinder/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",
+      "peer": true,
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.3",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+      "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
+      "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.8",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-calc": {
+      "version": "8.2.4",
+      "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz",
+      "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-selector-parser": "^6.0.9",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.2"
+      }
+    },
+    "node_modules/postcss-colormin": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz",
+      "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.21.4",
+        "caniuse-api": "^3.0.0",
+        "colord": "^2.9.1",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-convert-values": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz",
+      "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.21.4",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-discard-comments": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz",
+      "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-discard-duplicates": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz",
+      "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-discard-empty": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz",
+      "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-discard-overridden": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz",
+      "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-loader": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
+      "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cosmiconfig": "^7.0.0",
+        "klona": "^2.0.5",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "postcss": "^7.0.0 || ^8.0.1",
+        "webpack": "^5.0.0"
+      }
+    },
+    "node_modules/postcss-loader/node_modules/cosmiconfig": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "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"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/postcss-loader/node_modules/semver": {
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+      "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/postcss-merge-longhand": {
+      "version": "5.1.7",
+      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz",
+      "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0",
+        "stylehacks": "^5.1.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-merge-rules": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz",
+      "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.21.4",
+        "caniuse-api": "^3.0.0",
+        "cssnano-utils": "^3.1.0",
+        "postcss-selector-parser": "^6.0.5"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-minify-font-values": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz",
+      "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-minify-gradients": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz",
+      "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "colord": "^2.9.1",
+        "cssnano-utils": "^3.1.0",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-minify-params": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz",
+      "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.21.4",
+        "cssnano-utils": "^3.1.0",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-minify-selectors": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz",
+      "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-selector-parser": "^6.0.5"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-modules-extract-imports": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+      "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/postcss-modules-local-by-default": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz",
+      "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "icss-utils": "^5.0.0",
+        "postcss-selector-parser": "^7.0.0",
+        "postcss-value-parser": "^4.1.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+      "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss-modules-scope": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz",
+      "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "postcss-selector-parser": "^7.0.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+      "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss-modules-values": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+      "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "icss-utils": "^5.0.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/postcss-normalize-charset": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
+      "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-normalize-display-values": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz",
+      "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-normalize-positions": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz",
+      "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-normalize-repeat-style": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz",
+      "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-normalize-string": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz",
+      "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-normalize-timing-functions": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz",
+      "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-normalize-unicode": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz",
+      "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.21.4",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-normalize-url": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz",
+      "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "normalize-url": "^6.0.1",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-normalize-whitespace": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz",
+      "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-ordered-values": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz",
+      "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cssnano-utils": "^3.1.0",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-reduce-initial": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz",
+      "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.21.4",
+        "caniuse-api": "^3.0.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-reduce-transforms": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz",
+      "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "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",
+      "peer": true,
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss-svgo": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz",
+      "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.2.0",
+        "svgo": "^2.7.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "node_modules/postcss-unique-selectors": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz",
+      "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "postcss-selector-parser": "^6.0.5"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "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",
+      "peer": true
+    },
+    "node_modules/prettier": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+      "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "peer": true,
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/pretty-error": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+      "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "lodash": "^4.17.20",
+        "renderkid": "^3.0.0"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/progress-webpack-plugin": {
+      "version": "1.0.16",
+      "resolved": "https://registry.npmjs.org/progress-webpack-plugin/-/progress-webpack-plugin-1.0.16.tgz",
+      "integrity": "sha512-sdiHuuKOzELcBANHfrupYo+r99iPRyOnw15qX+rNlVUqXGfjXdH4IgxriKwG1kNJwVswKQHMdj1hYZMcb9jFaA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "chalk": "^2.1.0",
+        "figures": "^2.0.0",
+        "log-update": "^2.3.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "peerDependencies": {
+        "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/progress-webpack-plugin/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/progress-webpack-plugin/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/progress-webpack-plugin/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/progress-webpack-plugin/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/progress-webpack-plugin/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/progress-webpack-plugin/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-addr/node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/pump": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
+      "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+      "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "peer": true,
+      "dependencies": {
+        "side-channel": "^1.0.6"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "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/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+      "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/read-pkg": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+      "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/normalize-package-data": "^2.4.0",
+        "normalize-package-data": "^2.5.0",
+        "parse-json": "^5.0.0",
+        "type-fest": "^0.6.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg-up": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+      "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "find-up": "^4.1.0",
+        "read-pkg": "^5.2.0",
+        "type-fest": "^0.8.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/type-fest": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+      "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/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/relateurl": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+      "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/renderkid": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+      "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "css-select": "^4.1.3",
+        "dom-converter": "^0.2.0",
+        "htmlparser2": "^6.1.0",
+        "lodash": "^4.17.21",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/requires-port": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+      "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/resolve": {
+      "version": "1.22.10",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+      "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-core-module": "^2.16.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "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/restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/retry": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+      "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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",
+      "peer": true,
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "2.79.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
+      "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "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-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "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/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/schema-utils": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+      "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.5",
+        "ajv": "^6.12.4",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 8.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/select-hose": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+      "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/selfsigned": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz",
+      "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/node-forge": "^1.3.0",
+        "node-forge": "^1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "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/send": {
+      "version": "0.19.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+      "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "2.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/send/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/send/node_modules/debug/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/send/node_modules/encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "node_modules/serve-index": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+      "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "accepts": "~1.3.4",
+        "batch": "0.6.1",
+        "debug": "2.6.9",
+        "escape-html": "~1.0.3",
+        "http-errors": "~1.6.2",
+        "mime-types": "~2.1.17",
+        "parseurl": "~1.3.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/serve-index/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/serve-index/node_modules/depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/serve-index/node_modules/http-errors": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+      "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.0",
+        "statuses": ">= 1.4.0 < 2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/serve-index/node_modules/inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/serve-index/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/serve-index/node_modules/setprototypeof": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/serve-index/node_modules/statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/serve-static": {
+      "version": "1.16.2",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
+      "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.19.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true
+    },
+    "node_modules/shallow-clone": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "kind-of": "^6.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/shell-quote": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
+      "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+      "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/sirv": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
+      "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@polka/url": "^1.0.0-next.24",
+        "mrmime": "^2.0.0",
+        "totalist": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/sockjs": {
+      "version": "0.3.24",
+      "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+      "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "faye-websocket": "^0.11.3",
+        "uuid": "^8.3.2",
+        "websocket-driver": "^0.7.4"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/spdx-correct": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+      "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-exceptions": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
+      "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
+      "dev": true,
+      "license": "CC-BY-3.0"
+    },
+    "node_modules/spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-license-ids": {
+      "version": "3.0.21",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
+      "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
+      "dev": true,
+      "license": "CC0-1.0"
+    },
+    "node_modules/spdy": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+      "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "debug": "^4.1.0",
+        "handle-thing": "^2.0.0",
+        "http-deceiver": "^1.2.7",
+        "select-hose": "^2.0.0",
+        "spdy-transport": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/spdy-transport": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+      "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "debug": "^4.1.0",
+        "detect-node": "^2.0.4",
+        "hpack.js": "^2.1.6",
+        "obuf": "^1.1.2",
+        "readable-stream": "^3.0.6",
+        "wbuf": "^1.7.3"
+      }
+    },
+    "node_modules/ssri": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
+      "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "dependencies": {
+        "minipass": "^3.1.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/stable": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+      "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/stackframe": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
+      "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-eof": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/stylehacks": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
+      "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "browserslist": "^4.21.4",
+        "postcss-selector-parser": "^6.0.4"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.15"
+      }
+    },
+    "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/svgo": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
+      "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@trysound/sax": "0.2.0",
+        "commander": "^7.2.0",
+        "css-select": "^4.1.3",
+        "css-tree": "^1.1.3",
+        "csso": "^4.2.0",
+        "picocolors": "^1.0.0",
+        "stable": "^0.1.8"
+      },
+      "bin": {
+        "svgo": "bin/svgo"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/svgo/node_modules/commander": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/tapable": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+      "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/terser": {
+      "version": "5.39.0",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
+      "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser-webpack-plugin": {
+      "version": "5.3.12",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.12.tgz",
+      "integrity": "sha512-jDLYqo7oF8tJIttjXO6jBY5Hk8p3A8W4ttih7cCEq64fQFWmgJ4VqAQjKr7WwIDlmXKEc6QeoRb5ecjZ+2afcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "jest-worker": "^27.4.5",
+        "schema-utils": "^4.3.0",
+        "serialize-javascript": "^6.0.2",
+        "terser": "^5.31.1"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        },
+        "uglify-js": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/terser-webpack-plugin/node_modules/schema-utils": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
+      "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/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",
+      "peer": true,
+      "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",
+      "peer": true,
+      "dependencies": {
+        "thenify": ">= 3.1.0 < 4"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/thread-loader": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-3.0.4.tgz",
+      "integrity": "sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "json-parse-better-errors": "^1.0.2",
+        "loader-runner": "^4.1.0",
+        "loader-utils": "^2.0.0",
+        "neo-async": "^2.6.2",
+        "schema-utils": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^4.27.0 || ^5.0.0"
+      }
+    },
+    "node_modules/thread-loader/node_modules/loader-utils": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
+    "node_modules/thread-loader/node_modules/schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/thunky": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+      "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "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/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/totalist": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+      "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ts-loader": {
+      "version": "9.5.2",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz",
+      "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^5.0.0",
+        "micromatch": "^4.0.0",
+        "semver": "^7.3.4",
+        "source-map": "^0.7.4"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "typescript": "*",
+        "webpack": "^5.0.0"
+      }
+    },
+    "node_modules/ts-loader/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/ts-loader/node_modules/semver": {
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+      "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/ts-loader/node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "dev": true,
+      "license": "0BSD",
+      "peer": true
+    },
+    "node_modules/type-fest": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+      "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.8.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+      "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
+      "devOptional": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "6.20.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+      "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.1"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/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/utila": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+      "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/vite": {
+      "version": "3.2.11",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.11.tgz",
+      "integrity": "sha512-K/jGKL/PgbIgKCiJo5QbASQhFiV02X9Jh+Qq0AKCRCRKZtOTVi4t6wh75FDpGf2N9rYOnzH87OEFQNaFy6pdxQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.15.9",
+        "postcss": "^8.4.18",
+        "resolve": "^1.22.1",
+        "rollup": "^2.79.1"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      },
+      "peerDependencies": {
+        "@types/node": ">= 14",
+        "less": "*",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
+      "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/compiler-sfc": "3.5.13",
+        "@vue/runtime-dom": "3.5.13",
+        "@vue/server-renderer": "3.5.13",
+        "@vue/shared": "3.5.13"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-hot-reload-api": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
+      "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/vue-loader": {
+      "version": "17.4.2",
+      "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.4.2.tgz",
+      "integrity": "sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "hash-sum": "^2.0.0",
+        "watchpack": "^2.4.0"
+      },
+      "peerDependencies": {
+        "webpack": "^4.1.0 || ^5.0.0-0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/compiler-sfc": {
+          "optional": true
+        },
+        "vue": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-loader/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",
+      "peer": true,
+      "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/vue-style-loader": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
+      "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "hash-sum": "^1.0.2",
+        "loader-utils": "^1.0.2"
+      }
+    },
+    "node_modules/vue-style-loader/node_modules/hash-sum": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
+      "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/vue-template-es2015-compiler": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
+      "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/watchpack": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
+      "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/wbuf": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+      "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "minimalistic-assert": "^1.0.0"
+      }
+    },
+    "node_modules/wcwidth": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/webpack": {
+      "version": "5.98.0",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz",
+      "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/eslint-scope": "^3.7.7",
+        "@types/estree": "^1.0.6",
+        "@webassemblyjs/ast": "^1.14.1",
+        "@webassemblyjs/wasm-edit": "^1.14.1",
+        "@webassemblyjs/wasm-parser": "^1.14.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": "^4.3.0",
+        "tapable": "^2.1.1",
+        "terser-webpack-plugin": "^5.3.11",
+        "watchpack": "^2.4.1",
+        "webpack-sources": "^3.2.3"
+      },
+      "bin": {
+        "webpack": "bin/webpack.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependenciesMeta": {
+        "webpack-cli": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-bundle-analyzer": {
+      "version": "4.10.2",
+      "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz",
+      "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@discoveryjs/json-ext": "0.5.7",
+        "acorn": "^8.0.4",
+        "acorn-walk": "^8.0.0",
+        "commander": "^7.2.0",
+        "debounce": "^1.2.1",
+        "escape-string-regexp": "^4.0.0",
+        "gzip-size": "^6.0.0",
+        "html-escaper": "^2.0.2",
+        "opener": "^1.5.2",
+        "picocolors": "^1.0.0",
+        "sirv": "^2.0.3",
+        "ws": "^7.3.1"
+      },
+      "bin": {
+        "webpack-bundle-analyzer": "lib/bin/analyzer.js"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      }
+    },
+    "node_modules/webpack-bundle-analyzer/node_modules/commander": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/webpack-bundle-analyzer/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",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/webpack-chain": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.5.1.tgz",
+      "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==",
+      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+      "dev": true,
+      "license": "MPL-2.0",
+      "peer": true,
+      "dependencies": {
+        "deepmerge": "^1.5.2",
+        "javascript-stringify": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/webpack-chain/node_modules/deepmerge": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz",
+      "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/webpack-dev-middleware": {
+      "version": "5.3.4",
+      "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
+      "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "colorette": "^2.0.10",
+        "memfs": "^3.4.3",
+        "mime-types": "^2.1.31",
+        "range-parser": "^1.2.1",
+        "schema-utils": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/webpack-dev-middleware/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/webpack-dev-middleware/node_modules/schema-utils": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
+      "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/webpack-dev-server": {
+      "version": "4.15.2",
+      "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
+      "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/bonjour": "^3.5.9",
+        "@types/connect-history-api-fallback": "^1.3.5",
+        "@types/express": "^4.17.13",
+        "@types/serve-index": "^1.9.1",
+        "@types/serve-static": "^1.13.10",
+        "@types/sockjs": "^0.3.33",
+        "@types/ws": "^8.5.5",
+        "ansi-html-community": "^0.0.8",
+        "bonjour-service": "^1.0.11",
+        "chokidar": "^3.5.3",
+        "colorette": "^2.0.10",
+        "compression": "^1.7.4",
+        "connect-history-api-fallback": "^2.0.0",
+        "default-gateway": "^6.0.3",
+        "express": "^4.17.3",
+        "graceful-fs": "^4.2.6",
+        "html-entities": "^2.3.2",
+        "http-proxy-middleware": "^2.0.3",
+        "ipaddr.js": "^2.0.1",
+        "launch-editor": "^2.6.0",
+        "open": "^8.0.9",
+        "p-retry": "^4.5.0",
+        "rimraf": "^3.0.2",
+        "schema-utils": "^4.0.0",
+        "selfsigned": "^2.1.1",
+        "serve-index": "^1.9.1",
+        "sockjs": "^0.3.24",
+        "spdy": "^4.0.2",
+        "webpack-dev-middleware": "^5.3.4",
+        "ws": "^8.13.0"
+      },
+      "bin": {
+        "webpack-dev-server": "bin/webpack-dev-server.js"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^4.37.0 || ^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "webpack": {
+          "optional": true
+        },
+        "webpack-cli": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-dev-server/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/webpack-dev-server/node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/webpack-dev-server/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/webpack-dev-server/node_modules/schema-utils": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
+      "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/webpack-dev-server/node_modules/ws": {
+      "version": "8.18.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
+      "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-merge": {
+      "version": "5.10.0",
+      "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz",
+      "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "clone-deep": "^4.0.1",
+        "flat": "^5.0.2",
+        "wildcard": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/webpack-sources": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+      "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/webpack-virtual-modules": {
+      "version": "0.4.6",
+      "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.6.tgz",
+      "integrity": "sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/webpack/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/webpack/node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/webpack/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/webpack/node_modules/schema-utils": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
+      "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/webpack/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/websocket-driver": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+      "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "dependencies": {
+        "http-parser-js": ">=0.5.1",
+        "safe-buffer": ">=5.1.0",
+        "websocket-extensions": ">=0.1.1"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/websocket-extensions": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+      "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/whatwg-fetch": {
+      "version": "3.6.20",
+      "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
+      "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
+      }
+    },
+    "node_modules/wildcard": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
+      "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "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/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/ws": {
+      "version": "7.5.10",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+      "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8.3.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/yaml": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      }
+    }
+  }
+}

diff --git a/frontend/package.json b/frontend/package.json
line changes: +29/-0
index 0000000..fcbac67
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,29 @@
+{
+  "name": "frontend",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "type-check": "vue-tsc --noEmit",
+    "build-check": "vue-tsc --noEmit && vite build",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@xterm/addon-fit": "^0.10.0",
+    "@xterm/addon-web-links": "^0.11.0",
+    "@xterm/xterm": "^5.5.0",
+    "js-yaml": "^4.1.0",
+    "vue": "^3.2.37"
+  },
+  "devDependencies": {
+    "@types/node": "^22.13.8",
+    "@vitejs/plugin-vue": "^3.2.0",
+    "@vue/cli-plugin-typescript": "^5.0.8",
+    "@vue/runtime-core": "^3.5.13",
+    "@vue/tsconfig": "^0.7.0",
+    "typescript": "^5.8.2",
+    "vite": "^3.0.7"
+  }
+}

diff --git a/frontend/package.json.md5 b/frontend/package.json.md5
line changes: +1/-0
index 0000000..70b2817
--- /dev/null
+++ b/frontend/package.json.md5
@@ -0,0 +1 @@
+578bffc0710f3ba42676abfbea4b0d3d 
\ No newline at end of file

diff --git a/frontend/src/App.vue b/frontend/src/App.vue
line changes: +602/-0
index 0000000..6b376ee
--- /dev/null
+++ b/frontend/src/App.vue
@@ -0,0 +1,602 @@
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { SecretUpdateOptions } from './types/custom';
+import NavigationPane from './components/NavigationPane.vue';
+import DeploymentsList from './components/DeploymentsList.vue';
+import StatefulSetsList from './components/StatefulsetsList.vue';
+import PodsList from './components/PodsList.vue';
+import ConfigMapsList from './components/ConfigMapsList.vue';
+import IngressesList from './components/IngressesList.vue';
+import NamespacesList from './components/NamespacesList.vue';
+import SecretsList from './components/SecretsList.vue';
+import PersistentVolumeClaimsList from './components/PersistentVolumeClaimsList.vue';
+import PersistentVolumesList from './components/PersistentVolumesList.vue';
+import BottomTabs from './components/BottomTabs.vue';
+import { CustomPod } from './types/custom';
+import { KubernetesPod } from './types/kubernetes';
+import { kubernetesService } from './lib/kubernetes';
+import {
+  KubernetesCluster,
+  KubernetesDeployment,
+  KubernetesStatefulSet,
+  KubernetesConfigMap,
+  KubernetesIngress,
+  KubernetesNamespace,
+  KubernetesSecret,
+  KubernetesPersistentVolumeClaim,
+  KubernetesPersistentVolume,
+  ApiResponse,
+  } from './types/kubernetes';
+
+export default defineComponent({
+  components: {
+    NavigationPane,
+    DeploymentsList,
+    StatefulSetsList,
+    PodsList,
+    ConfigMapsList,
+    IngressesList,
+    NamespacesList,
+    SecretsList,
+    PersistentVolumeClaimsList,
+    PersistentVolumesList,
+    BottomTabs
+  },
+
+  computed: {
+    currentResourceComponent() {
+      if (this.selectedResource === 'pods') {
+        return 'PodsList';
+      } else if (this.selectedResource === 'deployments') {
+        return 'DeploymentsList';
+      } else if (this.selectedResource === 'statefulsets') {
+        return 'StatefulSetsList';
+      } else if (this.selectedResource === 'configmaps') {
+        return 'ConfigMapsList';
+      } else if (this.selectedResource === 'namespaces') {
+        return 'NamespacesList';
+      } else if (this.selectedResource === 'secrets') {
+        return 'SecretsList';
+      } else if (this.selectedResource === 'persistentvolumeclaims') {
+        return 'PersistentVolumeClaimsList';
+      } else if (this.selectedResource === 'persistentVolumes') {
+        return 'PersistentVolumesList';
+      } else {
+        return 'IngressesList';
+      }
+    }
+  },
+
+  data() {
+    return {
+      clusters: [] as KubernetesCluster[],
+      selectedCluster: null as KubernetesCluster | null,
+      selectedResource: null as string | null,
+      selectedResourceItem: null as any | null,
+      deployments: [] as KubernetesDeployment[],
+      statefulSets: [] as KubernetesStatefulSet[],
+      pods: [] as any[],
+      configMaps: [] as KubernetesConfigMap[],
+      ingresses: [] as KuberntesIngress[],
+      namespaces: [] as KubernetesNamespaces[],
+      secrets: [] as KubernetesSecret[],
+      persistentvolumeclaims: [] as KubernetesPersistentVolumeClaim[],
+      persistentVolumes: [] as KubernetesPersistentVolume[],
+      selectedTab: 'logs' as 'logs' | 'terminal' | 'describe',
+    };
+  },
+
+  async created() {
+    await this.loadClusters();
+  },
+
+  methods: {
+    handleStatefulSetSelect(statefulSet: KubernetesStatefulSet) {
+      this.selectedResourceItem = statefulSet;
+    },
+
+    handleDeploymentSelect(deployment: KubernetesDeployment) {
+      this.selectedResourceItem = deployment;
+    },
+
+    handlePodSelect(pod: any) {
+      this.selectedResourceItem = pod;
+    },
+
+    handleConfigMapSelect(configMap: KubernetesConfigMap) {
+      this.selectedResourceItem = configMap;
+    },
+
+    handleIngressSelect(ingress: KubernetesIngress) {
+      this.selectedResourceItem = ingress;
+    },
+
+    handleNamespaceSelect(namespace: KubernetesNamespace) {
+      this.selectedResourceItem = namespace;
+    },
+
+    handleSecretSelect(secret: KubernetesSecret) {
+      this.selectedResourceItem = secret;
+    },
+
+    handlePersistentVolumeClaimSelect(persistentVolumeClaim: KubernetesPersistentVolumeClaim) {
+      this.selectedResourceItem = persistentVolumeClaim;
+    },
+
+    handlePersistentVolumeSelect(persistentVolume: KubernetesPersistentVolume) {
+      this.selectedResourceItem = persistentVolume;
+    },
+
+    async loadClusters() {
+      try {
+        const response = await kubernetesService.getClusters();
+        if (response.success && response.data) {
+          this.clusters = response.data.map(cluster => ({
+            ...cluster,
+            name: cluster.name
+          }));
+        }
+      } catch (error) {
+        console.error('Failed to load clusters:', error);
+      }
+    },
+
+    getClusterName(server: string): string {
+      try {
+        const url = new URL(server);
+        return url.hostname;
+      } catch {
+        return server;
+      }
+    },
+
+    async handleUpdateSecret(opts: SecretUpdateOptions) {
+      try {
+        const response = await kubernetesService.updateSecret(opts.context, opts.definition, opts.origNamespace, opts.origName);
+        if (response.success) {
+          alert(response.msg);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error: any) {
+        alert(`Error updating Secret: ${error}`);
+      }
+    },
+
+    async handleDeleteSecret({cluster, secret}: { cluster: KubernetesCluster, secret: KubernetesSecret}) {
+      try {
+        const response = await kubernetesService.deleteSecret(cluster.contextName, secret.metadata.namespace, secret.metadata.name);
+        if (response.success) {
+          alert(response.msg);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        alert(`Error deleting Secret: ${error}`);
+      }
+    },
+
+    async handleDeleteNamespace({cluster, namespace}: { cluster: KubernetesCluster, namespace: KubernetesNamespace }) {
+      try {
+        const response = await kubernetesService.deleteNamespace(cluster.contextName, namespace.metadata.name);
+        if (response.success) {
+          alert(`Namespace ${namespace.metadata.name} deleted successfully`);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg || 'Unknown error');
+        }
+      } catch (error) {
+        alert(`Error deleting Namespace: ${error}`);
+      }
+    },
+
+    async handleDeletePersistentVolume({cluster, definition}: { cluster: KubernetesCluster, definition: KubernetesPersistentVolume}) {
+      try {
+        const response = await kubernetesService.deletePersistentVolume(cluster.contextName, definition.metadata.name);
+        if (response.success) {
+          alert(response.msg);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        alert(`Error deleting Persistent Volume: ${error}`);
+      }
+    },
+
+    async handleCreateSecret(opts: SecretCreateOptions) {
+      try {
+        let response;
+        response = await kubernetesService.createSecret(opts.context, opts.opts);
+        if (response.success) {
+          alert(response.msg)
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        alert(`Error creating Secret: ${error}`);
+      }
+    },
+
+    async handleCreatePersistentVolumeClaim({cluster, persistentVolumeClaim}: {cluster: KubernetesCluster, persistentVolumeClaim: KubernetesPersistentVolumeClaim}) {
+      try {
+        let response;
+        response = await kubernetesService.createPersistentVolumeClaim(cluster.contextName, persistentVolumeClaim);
+        if (response.success) {
+          alert(response.msg)
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        alert(`Error creating Persistent Volume Claim: ${error}`);
+      }
+    },
+
+    async handleDeletePersistentVolumeClaim({cluster, persistentVolumeClaim}: { cluster: KubernetesCluster, persistentVolumeClaim: KubernetesPersistentVolumeClaim}) {
+      try {
+        const response = await kubernetesService.deletePersistentVolumeClaim(cluster.contextName, persistentVolumeClaim.metadata.namespace, persistentVolumeClaim.metadata.name);
+        if (response.success) {
+          alert(response.msg);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        alert(`Error deleting Persistent Volume Claim: ${error}`);
+      }
+    },
+
+    async handleCreatePersistentVolume({cluster, definition}: {cluster: KubernetesCluster, definition: KubernetesPersistentVolume}) {
+      try {
+        let response;
+        response = await kubernetesService.createPersistentVolume(cluster.contextName, definition);
+        if (response.success) {
+          alert(response.msg)
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        alert(`Error creating Persistent Volume: ${error}`);
+      }
+    },
+
+    async handleCreateNamespace(
+      {cluster, definition, yamlContent, isYaml}: 
+      {
+        cluster: { contextName: string },
+        definition?: any,
+        yamlContent?: string,
+        isYaml?: boolean,
+      }
+    ) {
+      try {
+        let response;
+        if (isYaml && yamlContent) {
+          response = await kubernetesService.createNamespaceYAML(cluster.contextName, yamlContent);
+        } else if (!isYaml && definition) {
+          response = await kubernetesService.createNamespace(cluster.contextName, definition);
+        } else {
+          throw new Error("Invalid namespace creation parameters");
+        }
+
+        if (response.success) {
+          alert(response.msg)
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        alert(`Error creating namespace: ${error}`);
+      }
+    },
+
+    async handleCreatePod({
+      cluster,
+      podDefinition,
+      yamlContent,
+      isYaml
+    }: {
+      cluster: { contextName: string }, 
+      podDefinition?: any,
+      yamlContent?: string,
+      isYaml?: boolean
+    }) {
+      try {
+        let response;
+
+        if (isYaml && yamlContent) {
+          response = await kubernetesService.createPodYAML(cluster.contextName, yamlContent);
+        } else if (!isYaml && podDefinition) {
+          response = await kubernetesService.createPod(cluster.contextName, podDefinition);
+        } else {
+          throw new Error('Invalid pod creation parameters');
+        }
+
+        if (response.success) {
+          alert(response.msg)
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        console.error('Error creating pod:', error);
+        alert(`Error creating pod: ${error}`);
+      }
+    },
+
+    async handleDeletePod({cluster, pod}: { cluster: KubernetesCluster, pod: KubernetesPod }) {
+      try {
+        const response = await kubernetesService.deletePod(cluster.contextName, pod.metadata.namespace, pod.metadata.name);
+        if (response.success) {
+          alert(`Pod ${pod.metadata.name} deleted successfully`);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg || 'Unknown error');
+        }
+      } catch (error) {
+        console.error('Error deleting pod:', error);
+        alert(`Error deleting pod: ${error}`);
+      }
+    },
+
+    async handleDeleteIngress({cluster, ingress}: { cluster: KubernetesCluster, ingress: KubernetesIngress }) {
+      try {
+        const response = await kubernetesService.deleteIngress(cluster.contextName, ingress.metadata.namespace, ingress.metadata.name);
+        if (response.success) {
+          alert(`Ingress ${ingress.metadata.name} deleted successfully`);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg);
+        }
+      } catch (error) {
+        console.error('Error deleting ingress:', error);
+        alert(`Error deleting ingress: ${error}`);
+      }
+    },
+
+    async handleRestartDeployment({ cluster, deployment }: { cluster: KubernetesCluster, deployment: KubernetesDeployment }) {
+      try {
+        const response = await kubernetesService.restartDeployment(cluster.contextName, deployment.metadata.namespace, deployment.metadata.name);
+        if (response.success) {
+          alert(`Deployment ${deployment.metadata.name} restarted successfully`);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg || 'Unknown error');
+        }
+      } catch (error) {
+        alert(`Error restarting deployment: ${error}`);
+      }
+    },
+
+    async handleRestartStatefulSet({ cluster, statefulSet }: { cluster: KubernetesCluster, statefulSet: KubernetesStatefulSet }) {
+      try {
+        const response = await kubernetesService.restartStatefulSet(cluster.contextName, statefulSet.metadata.namespace, statefulSet.metadata.name);
+        if (response.success) {
+          alert(`StatefulSet ${statefulSet.metadata.name} restarted successfully`);
+          await this.loadResources();
+        } else {
+          throw new Error(response.msg || 'Unknown error');
+        }
+      } catch (error) {
+        alert(`Error restarting statefulset: ${error}`);
+      }
+    },
+
+    handleClusterSelect(cluster: KubernetesCluster) {
+      this.selectedCluster = cluster;
+      this.selectedResource = null;
+      this.deployments = [];
+      this.pods = [];
+      this.statefulSets = [];
+      this.configMaps = [];
+      console.log("selected cluster: " + cluster.contextName);
+    },
+
+    handleResourceSelect(resource: string) {
+      this.selectedResource = resource;
+      this.selectedResourceItem = null; // Reset selected item when changing resource type
+      this.loadResources();
+    },
+
+    handleResourceItemSelect(item: any) {
+      this.selectedResourceItem = item;
+    },
+
+    handleTabChange(tab: 'logs' | 'terminal' | 'describe') {
+      this.selectedTab = tab;
+    },
+
+    async loadResources() {
+      if (!this.selectedCluster || !this.selectedResource) return;
+
+      try {
+        if (this.selectedResource === 'deployments') {
+          try {
+            const response = await kubernetesService.getDeployments(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.deployments = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load deployments:', error);
+          }
+        } else if (this.selectedResource === 'pods') {
+          // Clear existing pods first
+          this.pods = [];
+          try {
+            const response = await kubernetesService.getPods(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.pods = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load pods:', error);
+          }
+        } else if (this.selectedResource === 'statefulsets') {
+          try {
+            const response = await kubernetesService.getStatefulSets(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.statefulSets = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load statefulsets:', error);
+          }
+        } else if (this.selectedResource === 'configmaps') {
+          try {
+            const response = await kubernetesService.getConfigMaps(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.configMaps = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load configmaps:', error);
+          }
+        } else if (this.selectedResource === 'ingresses') {
+          try {
+            const response = await kubernetesService.getIngresses(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.ingresses = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load ingresses:', error);
+          }
+        } else if (this.selectedResource === 'namespaces') {
+          try {
+            const response = await kubernetesService.getNamespaces(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.namespaces = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load namespaces:', error);
+          }
+        } else if (this.selectedResource === 'secrets') {
+          try {
+            const response = await kubernetesService.listSecrets(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.secrets = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load Secrets:', error);
+          }
+        } else if (this.selectedResource === 'persistentvolumeclaims') {
+          try {
+            const response = await kubernetesService.listPersistentVolumeClaims(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.persistentvolumeclaims = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load Persistent Volume Claims:', error);
+          }
+        } else if (this.selectedResource === 'persistentVolumes') {
+          try {
+            const response = await kubernetesService.listPersistentVolumes(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.persistentVolumes = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load Persistent Volumes:', error);
+          }
+
+          try {
+            const response = await kubernetesService.listPersistentVolumeClaims(this.selectedCluster.contextName);
+            if (response.success && response.data) {
+              this.persistentvolumeclaims = response.data;
+            }
+          } catch (error) {
+            console.error('Failed to load Persistent Volume Claims:', error);
+          }
+        }
+      } catch (error) {
+        console.error('Failed to load resources:', error);
+      }
+    },
+  },
+});
+</script>
+
+<template>
+  <div class="app-container">
+    <NavigationPane 
+      :clusters="clusters"
+      :selectedCluster="selectedCluster"
+      :selectedResource="selectedResource"
+      @select-cluster="handleClusterSelect"
+      @select-resource="handleResourceSelect"
+    />
+    <div class="right-container">
+      <component 
+        :is="currentResourceComponent"
+        :selectedCluster="selectedCluster"
+        :deployments="deployments"
+        :statefulSets="statefulSets"
+        :configMaps="configMaps"
+        :pods="pods"
+        :ingresses="ingresses"
+        :namespaces="namespaces"
+        :secrets="secrets"
+        :persistentvolumeclaims="persistentvolumeclaims"
+        :persistentVolumes="persistentVolumes"
+        @create-namespace="handleCreateNamespace"
+        @create-pod="handleCreatePod"
+        @create-secret="handleCreateSecret"
+        @create-persistentvolume="handleCreatePersistentVolume"
+        @create-persistentvolumeclaim="handleCreatePersistentVolumeClaim"
+        @delete-ingress="handleDeleteIngress"
+        @delete-namespace="handleDeleteNamespace"
+        @delete-pod="handleDeletePod"
+        @delete-secret="handleDeleteSecret"
+        @delete-persistentvolume="handleDeletePersistentVolume"
+        @delete-persistentvolumeclaim="handleDeletePersistentVolumeClaim"
+        @load-resources="loadResources"
+        @restart-deployment="handleRestartDeployment"
+        @restart-statefulset="handleRestartStatefulSet"
+
+        @pod-selected="handlePodSelect"
+        @deployment-selected="handleDeploymentSelect"
+        @configmap-selected="handleConfigMapSelect"
+        @namespace-selected="handleNamespaceSelect"
+        @ingress-selected="handleIngressSelect"
+        @secret-selected="handleSecretSelect"
+        @statefulset-selected="handleStatefulSetSelect"
+        @persistentVolumeClaim-selected="handlePersistentVolumeClaimSelect"
+        @persistentVolume-selected="handlePersistentVolumeSelect"
+
+        @update-secret="handleUpdateSecret"
+      />
+      <BottomTabs
+        v-if="selectedResourceItem"
+        :selectedResource="selectedResourceItem"
+        :selectedCluster="selectedCluster"
+        :selectedTab="selectedTab"
+        @tab-changed="handleTabChange"
+      />
+    </div>
+  </div>
+</template>
+
+<style>
+.app-container {
+  display: flex;
+  height: 100vh;
+  background-color: #1e1e1e;
+  color: #ffffff;
+}
+
+.right-container {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  border-left: 1px solid #333333;
+}
+
+.right-container > :first-child {
+  flex: 1;
+  overflow-y: auto;
+}
+
+.right-container > :last-child {
+  border-top: 1px solid #333333;
+}
+</style>

diff --git a/frontend/src/assets/css/BottomTabs.css b/frontend/src/assets/css/BottomTabs.css
line changes: +122/-0
index 0000000..b53e1ce
--- /dev/null
+++ b/frontend/src/assets/css/BottomTabs.css
@@ -0,0 +1,122 @@
+/* Style section remains the same as in the previous response */
+.bottom-tabs {
+  display: flex;
+  flex-direction: column;
+  background-color: #1e1e1e;
+  position: relative;
+  min-height: 100px;
+  max-height: 80vh;
+  width: 100%; /* Ensure it takes the full width of its container */
+  overflow: hidden; /* Prevent content from overflowing */
+}
+
+.resize-handle {
+  position: absolute;
+  top: -5px;
+  left: 0;
+  right: 0;
+  height: 10px;
+  cursor: ns-resize;
+  z-index: 10;
+}
+
+.tabs-header {
+  display: flex;
+  justify-content: space-between;
+  background-color: #252526;
+  border-bottom: 1px solid #333333;
+  width: 100%; /* Ensure it takes the full width */
+}
+
+.tabs {
+  display: flex;
+}
+
+.tabs button {
+  padding: 8px 16px;
+  background: none;
+  border: none;
+  color: #cccccc;
+  cursor: pointer;
+  font-size: 13px;
+  border-right: 1px solid #333333;
+  text-align: left;
+}
+
+.tabs button:hover {
+  background-color: #2a2d2e;
+}
+
+.tabs button.active {
+  background-color: #37373d;
+  color: #ffffff;
+}
+
+.tabs-actions {
+  display: flex;
+  align-items: center;
+  padding-right: 8px;
+}
+
+.tabs-actions button {
+  background: none;
+  border: none;
+  color: #cccccc;
+  cursor: pointer;
+  padding: 4px 8px;
+  margin-left: 8px;
+}
+
+.tabs-actions button:hover {
+  background-color: #2a2d2e;
+}
+
+.tabs-actions select {
+  background-color: #3c3c3c;
+  color: #cccccc;
+  border: 1px solid #555555;
+  padding: 4px 8px;
+  margin-left: 8px;
+  border-radius: 2px;
+}
+
+.tab-content {
+  flex: 1;
+  overflow: auto;
+  padding: 8px;
+  font-family: 'Consolas', 'Courier New', monospace;
+  font-size: 13px;
+  width: 100%; /* Ensure it takes the full width */
+  box-sizing: border-box; /* Include padding in width calculation */
+}
+
+.loading, .error, .terminal-placeholder {
+  padding: 16px;
+  color: #cccccc;
+}
+
+.error {
+  color: #f44336;
+}
+
+pre {
+  margin: 0;
+  white-space: pre-wrap; /* Allow text to wrap */
+  word-break: break-all;
+  max-width: 100%; /* Ensure it doesn't exceed container width */
+}
+
+.left-aligned {
+  text-align: left;
+}
+
+.terminal-style {
+  font-family: 'Consolas', 'Courier New', monospace;
+  background-color: #1e1e1e;
+  color: #cccccc;
+  padding: 0;
+  line-height: 1.2;
+  /* Changed from pre to pre-wrap to ensure text wrapping */
+  white-space: pre-wrap;
+  overflow-wrap: break
+}

diff --git a/frontend/src/assets/css/ConfigMapsList.css b/frontend/src/assets/css/ConfigMapsList.css
line changes: +74/-0
index 0000000..4919184
--- /dev/null
+++ b/frontend/src/assets/css/ConfigMapsList.css
@@ -0,0 +1,74 @@
+.configmaps-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.search-bar-container {
+  flex-shrink: 0;
+  width: 100%;
+  background-color: #2a2a2a;
+  border-bottom: 1px solid #444;
+  z-index: 10;
+  box-sizing: border-box;
+  padding: 0;
+}
+
+.table-scroll-container {
+  flex: 1;
+  overflow-y: auto;
+  overflow-x: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.table-container {
+  height: 100%;
+  overflow-y: auto;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 13px;
+}
+
+th, td {
+  padding: 8px 16px;
+  text-align: left;
+  border-bottom: 1px solid #333333;
+}
+
+th {
+  position: sticky;
+  top: 0;
+  font-weight: 500;
+  color: #858585;
+  background-color: #252526;
+  z-index: 1;
+}
+
+td {
+  color: #cccccc;
+}
+
+tr:hover td {
+  background-color: #2a2d2e;
+}
+
+tr.selected td {
+  background-color: #37373d;
+}
+
+.empty-state {
+  padding: 24px;
+  text-align: center;
+  color: #666;
+  background-color: #f9f9f9;
+  border-radius: 4px;
+  margin-top: 16px;
+}

diff --git a/frontend/src/assets/css/CreatePodGuided.css b/frontend/src/assets/css/CreatePodGuided.css
line changes: +158/-0
index 0000000..83963be
--- /dev/null
+++ b/frontend/src/assets/css/CreatePodGuided.css
@@ -0,0 +1,158 @@
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.7);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.modal-content {
+  background-color: #252526;
+  border-radius: 4px;
+  width: 500px;
+  max-width: 90%;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  border: 1px solid #3c3c3c;
+  color: #cccccc;
+}
+
+.modal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 16px;
+  border-bottom: 1px solid #3c3c3c;
+  background-color: #2a2a2a;
+}
+
+.modal-header h3 {
+  margin: 0;
+  font-size: 16px;
+  font-weight: 500;
+  color: #cccccc;
+}
+
+.close-button {
+  background: none;
+  border: none;
+  font-size: 20px;
+  cursor: pointer;
+  color: #858585;
+  padding: 0;
+  margin: 0;
+  line-height: 1;
+}
+
+.close-button:hover {
+  color: #cccccc;
+}
+
+.modal-body {
+  padding: 16px;
+}
+
+.modal-footer {
+  padding: 12px 16px;
+  border-top: 1px solid #3c3c3c;
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+  background-color: #2a2a2a;
+}
+
+.form-group {
+  margin-bottom: 16px;
+  text-align: left;
+}
+
+.form-group label {
+  display: block;
+  margin-bottom: 6px;
+  font-weight: normal;
+  font-size: 13px;
+  color: #cccccc;
+  text-align: left;
+  width: 100%;
+}
+
+.form-group input {
+  width: 100%;
+  padding: 8px 10px;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  font-size: 13px;
+  background-color: #1e1e1e;
+  color: #cccccc;
+  box-sizing: border-box;
+}
+
+.form-group input:focus {
+  outline: none;
+  border-color: #0e639c;
+}
+
+.namespace-display {
+  padding: 8px 10px;
+  background-color: #1e1e1e;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  font-size: 13px;
+  color: #cccccc;
+}
+
+.error-message {
+  color: #f44336;
+  margin-bottom: 12px;
+  padding: 8px 12px;
+  background-color: rgba(244, 67, 54, 0.1);
+  border-radius: 4px;
+  border-left: 3px solid #f44336;
+  font-size: 13px;
+}
+
+.cancel-button {
+  padding: 6px 12px;
+  background-color: #333333;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 13px;
+  color: #cccccc;
+  min-width: 80px;
+}
+
+.cancel-button:hover {
+  background-color: #3c3c3c;
+}
+
+.create-button {
+  padding: 6px 12px;
+  background-color: #0e639c;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 13px;
+  min-width: 80px;
+}
+
+.create-button:hover {
+  background-color: #1177bb;
+}
+
+.create-button:disabled, .cancel-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+/* Add placeholder styling to match the dark theme */
+::placeholder {
+  color: #666666;
+  opacity: 1;
+}
+

diff --git a/frontend/src/assets/css/CreatePodMethodSelector.css b/frontend/src/assets/css/CreatePodMethodSelector.css
line changes: +156/-0
index 0000000..3f9f4cc
--- /dev/null
+++ b/frontend/src/assets/css/CreatePodMethodSelector.css
@@ -0,0 +1,156 @@
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.7);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.modal-content {
+  background-color: #252526;
+  border-radius: 4px;
+  width: 380px; /* Reduced from 500px to match screenshot */
+  max-width: 90%;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  border: 1px solid #3c3c3c;
+  color: #cccccc;
+}
+
+.modal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 12px;
+  border-bottom: 1px solid #3c3c3c;
+  background-color: #2a2a2a;
+}
+
+.modal-header h3 {
+  margin: 0;
+  font-size: 14px;
+  font-weight: 500;
+  color: #cccccc;
+}
+
+.close-button {
+  background: none;
+  border: none;
+  font-size: 18px;
+  cursor: pointer;
+  color: #858585;
+  padding: 0;
+  margin: 0;
+  line-height: 1;
+}
+
+.close-button:hover {
+  color: #cccccc;
+}
+
+.modal-body {
+  padding: 8px 12px;
+}
+
+.modal-footer {
+  padding: 8px 12px;
+  border-top: 1px solid #3c3c3c;
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+  background-color: #2a2a2a;
+}
+
+.method-option {
+  padding: 6px 8px;
+  cursor: pointer;
+  border-radius: 2px;
+}
+
+.method-option:hover {
+  background-color: #2d2d2d;
+}
+
+.radio-container {
+  display: flex;
+  align-items: flex-start;
+}
+
+.radio-button {
+  margin-right: 8px;
+  margin-top: 2px; /* Align with first line of text */
+}
+
+.radio-outer {
+  width: 14px;
+  height: 14px;
+  border-radius: 50%;
+  border: 1px solid #858585;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.radio-inner {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  background-color: #0e639c;
+}
+
+.method-text {
+  display: flex;
+  flex-direction: column;
+}
+
+.method-label {
+  font-size: 13px;
+  color: #cccccc;
+  margin-bottom: 2px;
+}
+
+.method-description {
+  font-size: 12px;
+  color: #858585;
+  line-height: 1.3;
+}
+
+.cancel-button {
+  padding: 4px 10px;
+  background-color: #333333;
+  border: 1px solid #3c3c3c;
+  border-radius: 2px;
+  cursor: pointer;
+  font-size: 12px;
+  color: #cccccc;
+  min-width: 70px;
+}
+
+.cancel-button:hover {
+  background-color: #3c3c3c;
+}
+
+.continue-button {
+  padding: 4px 10px;
+  background-color: #0e639c;
+  color: white;
+  border: none;
+  border-radius: 2px;
+  cursor: pointer;
+  font-size: 12px;
+  min-width: 70px;
+}
+
+.continue-button:hover {
+  background-color: #1177bb;
+}
+
+.continue-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+

diff --git a/frontend/src/assets/css/CreatePodYaml.css b/frontend/src/assets/css/CreatePodYaml.css
line changes: +157/-0
index 0000000..177f2de
--- /dev/null
+++ b/frontend/src/assets/css/CreatePodYaml.css
@@ -0,0 +1,157 @@
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.7);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.modal-content {
+  background-color: #252526;
+  border-radius: 4px;
+  width: 500px;
+  max-width: 90%;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  border: 1px solid #3c3c3c;
+  color: #cccccc;
+  display: flex;
+  flex-direction: column;
+  max-height: 90vh;
+  overflow: hidden;
+}
+
+.yaml-editor {
+  width: 600px;
+  max-width: 90%;
+}
+
+.modal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 12px;
+  border-bottom: 1px solid #3c3c3c;
+  background-color: #2a2a2a;
+  flex-shrink: 0;
+}
+
+.modal-header h3 {
+  margin: 0;
+  font-size: 14px;
+  font-weight: 500;
+  color: #cccccc;
+}
+
+.close-button {
+  background: none;
+  border: none;
+  font-size: 18px;
+  cursor: pointer;
+  color: #858585;
+  padding: 0;
+  margin: 0;
+  line-height: 1;
+}
+
+.close-button:hover {
+  color: #cccccc;
+}
+
+.modal-body {
+  padding: 12px;
+  flex-grow: 1;
+  overflow: auto;
+}
+
+.modal-footer {
+  padding: 8px 12px;
+  border-top: 1px solid #3c3c3c;
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+  background-color: #2a2a2a;
+  flex-shrink: 0;
+}
+
+.yaml-container {
+  width: 100%;
+  height: 300px;
+  position: relative;
+}
+
+.yaml-textarea {
+  width: 100%;
+  height: 100%;
+  padding: 10px;
+  border: 1px solid #3c3c3c;
+  border-radius: 2px;
+  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
+  font-size: 13px;
+  line-height: 1.5;
+  resize: none;
+  background-color: #1e1e1e;
+  color: #cccccc;
+  box-sizing: border-box;
+  overflow: auto;
+}
+
+.yaml-textarea:focus {
+  outline: none;
+  border-color: #0e639c;
+}
+
+.error-message {
+  color: #f44336;
+  margin-bottom: 12px;
+  padding: 8px 12px;
+  background-color: rgba(244, 67, 54, 0.1);
+  border-radius: 2px;
+  border-left: 3px solid #f44336;
+  font-size: 13px;
+}
+
+.cancel-button {
+  padding: 4px 10px;
+  background-color: #333333;
+  border: 1px solid #3c3c3c;
+  border-radius: 2px;
+  cursor: pointer;
+  font-size: 12px;
+  color: #cccccc;
+  min-width: 70px;
+}
+
+.cancel-button:hover {
+  background-color: #3c3c3c;
+}
+
+.create-button {
+  padding: 4px 10px;
+  background-color: #0e639c;
+  color: white;
+  border: none;
+  border-radius: 2px;
+  cursor: pointer;
+  font-size: 12px;
+  min-width: 70px;
+}
+
+.create-button:hover {
+  background-color: #1177bb;
+}
+
+.create-button:disabled, .cancel-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+/* Add placeholder styling to match the dark theme */
+.yaml-textarea::placeholder {
+  color: #666666;
+  opacity: 1;
+}

diff --git a/frontend/src/assets/css/CreateResource.css b/frontend/src/assets/css/CreateResource.css
line changes: +273/-0
index 0000000..342fb69
--- /dev/null
+++ b/frontend/src/assets/css/CreateResource.css
@@ -0,0 +1,273 @@
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.7);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.modal-content {
+  background-color: #252526;
+  border-radius: 4px;
+  width: 380px; /* Reduced from 500px to match screenshot */
+  max-width: 90%;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  border: 1px solid #3c3c3c;
+  color: #cccccc;
+}
+
+.modal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 12px;
+  border-bottom: 1px solid #3c3c3c;
+  background-color: #2a2a2a;
+}
+
+.modal-header h3 {
+  margin: 0;
+  font-size: 14px;
+  font-weight: 500;
+  color: #cccccc;
+}
+
+.method-option {
+  padding: 6px 8px;
+  cursor: pointer;
+  border-radius: 2px;
+}
+
+.method-option:hover {
+  background-color: #2d2d2d;
+}
+
+.method-text {
+  display: flex;
+  flex-direction: column;
+}
+
+.method-label {
+  font-size: 13px;
+  color: #cccccc;
+  margin-bottom: 2px;
+}
+
+.method-description {
+  font-size: 12px;
+  color: #858585;
+  line-height: 1.3;
+}
+
+.radio-container {
+  display: flex;
+  align-items: flex-start;
+}
+
+.radio-button {
+  margin-right: 8px;
+  margin-top: 2px;
+}
+
+.radio-outer {
+  width: 14px;
+  height: 14px;
+  border-radius: 50%;
+  border: 1px solid #858585;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.radio-inner {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  background-color: #0e639c;
+}
+
+.close-button {
+  background: none;
+  border: none;
+  font-size: 20px;
+  cursor: pointer;
+  color: #858585;
+  padding: 0;
+  margin: 0;
+  line-height: 1;
+}
+
+.close-button:hover {
+  color: #cccccc;
+}
+
+.modal-body {
+  padding: 16px;
+}
+
+.modal-footer {
+  padding: 12px 16px;
+  border-top: 1px solid #3c3c3c;
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+  background-color: #2a2a2a;
+}
+
+.form-group {
+  margin-bottom: 16px;
+  text-align: left;
+}
+
+.form-group label {
+  display: block;
+  margin-bottom: 6px;
+  font-weight: normal;
+  font-size: 13px;
+  color: #cccccc;
+  text-align: left;
+  width: 100%;
+}
+
+.form-group input {
+  width: 100%;
+  padding: 8px 10px;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  font-size: 13px;
+  background-color: #1e1e1e;
+  color: #cccccc;
+  box-sizing: border-box;
+}
+
+.form-group input:focus {
+  outline: none;
+  border-color: #0e639c;
+}
+
+.namespace-display {
+  padding: 8px 10px;
+  background-color: #1e1e1e;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  font-size: 13px;
+  color: #cccccc;
+}
+
+.error-message {
+  color: #f44336;
+  margin-bottom: 12px;
+  padding: 8px 12px;
+  background-color: rgba(244, 67, 54, 0.1);
+  border-radius: 4px;
+  border-left: 3px solid #f44336;
+  font-size: 13px;
+}
+
+.cancel-button {
+  padding: 6px 12px;
+  background-color: #333333;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 13px;
+  color: #cccccc;
+  min-width: 80px;
+}
+
+.cancel-button:hover {
+  background-color: #3c3c3c;
+}
+
+.create-button {
+  padding: 6px 12px;
+  background-color: #0e639c;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 13px;
+  min-width: 80px;
+}
+
+.create-button:hover {
+  background-color: #1177bb;
+}
+
+.create-button:disabled, .cancel-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.continue-button {
+  padding: 4px 10px;
+  background-color: #0e639c;
+  color: white;
+  border: none;
+  border-radius: 2px;
+  cursor: pointer;
+  font-size: 12px;
+  min-width: 70px;
+}
+
+.continue-button:hover {
+  background-color: #1177bb;
+}
+
+.continue-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+/* Add placeholder styling to match the dark theme */
+::placeholder {
+  color: #666666;
+  opacity: 1;
+}
+
+.checkbox-label {
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  color: #cccccc;
+  font-size: 13px;
+  width: auto;
+  margin-bottom: 0;
+}
+
+.checkbox-label input {
+  margin-right: 8px;
+  cursor: pointer;
+  line-height: normal;
+}
+
+/* 
+   This selector is more specific than .form-group label 
+   It ensures the checkbox label doesn't behave like a block element 
+*/
+.form-group .checkbox-label {
+  display: flex !important;
+  align-items: center;
+  width: auto !important;  /* Critical: overrides width: 100% from .form-group label */
+  color: #cccccc;
+  font-size: 13px;
+  margin-bottom: 0;
+}
+
+/* 
+   This ensures the checkbox shrinks to its natural size 
+   instead of stretching to fill the parent container 
+*/
+.form-group .checkbox-label input {
+  width: auto !important;  /* Critical: overrides width: 100% from .form-group input */
+  margin-right: 8px;
+  cursor: pointer;
+  padding: 0;             /* Reset padding just in case */
+  border: none;           /* Reset border just in case */
+  background: none;       /* Reset background just in case */
+}

diff --git a/frontend/src/assets/css/DeploymentsList.css b/frontend/src/assets/css/DeploymentsList.css
line changes: +108/-0
index 0000000..3456de8
--- /dev/null
+++ b/frontend/src/assets/css/DeploymentsList.css
@@ -0,0 +1,108 @@
+.deployments-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  width: 100%; /* Ensure container takes full width */
+  box-sizing: border-box; /* Include padding in width calculation */
+}
+
+.search-bar-container {
+  flex-shrink: 0;
+  width: 100%;
+  background-color: #2a2a2a;
+  border-bottom: 1px solid #444;
+  z-index: 10;
+  box-sizing: border-box; /* Include padding in width calculation */
+  padding: 0; /* Remove any padding that might be causing overflow */
+}
+
+.table-scroll-container {
+  flex: 1;
+  overflow-y: auto;
+  overflow-x: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+
+/* Keep your existing table styles */
+.table-container {
+  height: 100%;
+  overflow-y: auto;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 13px;
+}
+
+th, td {
+  padding: 8px 16px;
+  text-align: left;
+  border-bottom: 1px solid #333333;
+}
+
+th {
+  position: sticky;
+  top: 0;
+  font-weight: 500;
+  color: #858585;
+  background-color: #252526;
+  z-index: 1;
+}
+
+td {
+  color: #cccccc;
+}
+
+tr:hover td {
+  background-color: #2a2d2e;
+}
+
+tr.selected td {
+  background-color: #37373d;
+}
+
+/* Context Menu Styles */
+.context-menu {
+  position: fixed;
+  background-color: #252526;
+  border: 1px solid #333333;
+  border-radius: 4px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
+  z-index: 1000;
+  min-width: 150px;
+}
+
+.menu-item {
+  padding: 8px 12px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.menu-item:hover {
+  background-color: #2a2d2e;
+}
+
+.menu-icon {
+  font-size: 16px;
+}
+
+.menu-text {
+  font-size: 14px;
+}
+
+/* Add this to your existing DeploymentsList.css */
+.empty-state {
+  padding: 24px;
+  text-align: center;
+  color: #666;
+  background-color: #f9f9f9;
+  border-radius: 4px;
+  margin-top: 16px;
+}

diff --git a/frontend/src/assets/css/EditPodYaml.css b/frontend/src/assets/css/EditPodYaml.css
line changes: +169/-0
index 0000000..69e12e3
--- /dev/null
+++ b/frontend/src/assets/css/EditPodYaml.css
@@ -0,0 +1,169 @@
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.7);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.modal-content {
+  background-color: #252526;
+  border-radius: 4px;
+  width: 500px;
+  max-width: 90%;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  border: 1px solid #3c3c3c;
+  color: #cccccc;
+  display: flex;
+  flex-direction: column;
+  max-height: 90vh;
+  overflow: hidden;
+}
+
+.yaml-editor {
+  width: 600px;
+  max-width: 90%;
+}
+
+.modal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 12px;
+  border-bottom: 1px solid #3c3c3c;
+  background-color: #2a2a2a;
+  flex-shrink: 0;
+}
+
+.modal-header h3 {
+  margin: 0;
+  font-size: 14px;
+  font-weight: 500;
+  color: #cccccc;
+}
+
+.close-button {
+  background: none;
+  border: none;
+  font-size: 18px;
+  cursor: pointer;
+  color: #858585;
+  padding: 0;
+  margin: 0;
+  line-height: 1;
+}
+
+.close-button:hover {
+  color: #cccccc;
+}
+
+.modal-body {
+  padding: 12px;
+  flex-grow: 1;
+  overflow: auto;
+}
+
+.modal-footer {
+  padding: 8px 12px;
+  border-top: 1px solid #3c3c3c;
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+  background-color: #2a2a2a;
+  flex-shrink: 0;
+}
+
+.yaml-container {
+  width: 100%;
+  height: 300px;
+  position: relative;
+}
+
+.yaml-textarea {
+  width: 100%;
+  height: 100%;
+  padding: 10px;
+  border: 1px solid #3c3c3c;
+  border-radius: 2px;
+  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
+  font-size: 13px;
+  line-height: 1.5;
+  resize: none;
+  background-color: #1e1e1e;
+  color: #cccccc;
+  box-sizing: border-box;
+  overflow: auto;
+}
+
+.yaml-textarea:focus {
+  outline: none;
+  border-color: #0e639c;
+}
+
+.error-message {
+  color: #f44336;
+  margin-bottom: 12px;
+  padding: 8px 12px;
+  background-color: rgba(244, 67, 54, 0.1);
+  border-radius: 2px;
+  border-left: 3px solid #f44336;
+  font-size: 13px;
+}
+
+.loading-message {
+  color: #42a5f5;
+  margin-bottom: 12px;
+  padding: 8px 12px;
+  background-color: rgba(66, 165, 245, 0.1);
+  border-radius: 2px;
+  border-left: 3px solid #42a5f5;
+  font-size: 13px;
+  text-align: center;
+}
+
+.cancel-button {
+  padding: 4px 10px;
+  background-color: #333333;
+  border: 1px solid #3c3c3c;
+  border-radius: 2px;
+  cursor: pointer;
+  font-size: 12px;
+  color: #cccccc;
+  min-width: 70px;
+}
+
+.cancel-button:hover {
+  background-color: #3c3c3c;
+}
+
+.update-button {
+  padding: 4px 10px;
+  background-color: #0e639c;
+  color: white;
+  border: none;
+  border-radius: 2px;
+  cursor: pointer;
+  font-size: 12px;
+  min-width: 70px;
+}
+
+.update-button:hover {
+  background-color: #1177bb;
+}
+
+.update-button:disabled, .cancel-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+/* Add placeholder styling to match the dark theme */
+.yaml-textarea::placeholder {
+  color: #666666;
+  opacity: 1;
+}
+

diff --git a/frontend/src/assets/css/IngressesList.css b/frontend/src/assets/css/IngressesList.css
line changes: +110/-0
index 0000000..f69a903
--- /dev/null
+++ b/frontend/src/assets/css/IngressesList.css
@@ -0,0 +1,110 @@
+.ingresses-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.search-bar-container {
+  flex-shrink: 0;
+  width: 100%;
+  background-color: #2a2a2a;
+  border-bottom: 1px solid #444;
+  z-index: 10;
+  box-sizing: border-box;
+  padding: 0;
+}
+
+.table-scroll-container {
+  flex: 1;
+  overflow-y: auto;
+  overflow-x: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.table-container {
+  height: 100%;
+  overflow-y: auto;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 13px;
+}
+
+th, td {
+  padding: 8px 16px;
+  text-align: left;
+  border-bottom: 1px solid #333333;
+}
+
+th {
+  position: sticky;
+  top: 0;
+  font-weight: 500;
+  color: #858585;
+  background-color: #252526;
+  z-index: 1;
+}
+
+td {
+  color: #cccccc;
+}
+
+tr:hover td {
+  background-color: #2a2d2e;
+}
+
+tr.selected td {
+  background-color: #37373d;
+}
+
+.empty-state {
+  padding: 24px;
+  text-align: center;
+  color: #666;
+  background-color: #f9f9f9;
+  border-radius: 4px;
+  margin-top: 16px;
+}
+
+.context-menu {
+  position: fixed;
+  background-color: #252526;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  z-index: 1000;
+  min-width: 120px;
+}
+
+.menu-item {
+  padding: 8px 12px;
+  cursor: pointer;
+  color: #cccccc;
+  font-size: 13px;
+  text-align: left;
+}
+
+.menu-item:hover {
+  background-color: #2a2d2e;
+}
+
+.menu-item.disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.menu-error {
+  padding: 8px 12px;
+  color: #f44336;
+  font-size: 12px;
+  border-top: 1px solid #3c3c3c;
+  max-width: 200px;
+  word-break: break-word;
+}

diff --git a/frontend/src/assets/css/ListResource.css b/frontend/src/assets/css/ListResource.css
line changes: +149/-0
index 0000000..d45e89c
--- /dev/null
+++ b/frontend/src/assets/css/ListResource.css
@@ -0,0 +1,149 @@
+.configmaps-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.search-bar-container {
+  flex-shrink: 0;
+  width: 100%;
+  background-color: #2a2a2a;
+  border-bottom: 1px solid #444;
+  z-index: 10;
+  box-sizing: border-box;
+  padding: 0;
+}
+
+.table-scroll-container {
+  flex: 1;
+  overflow-y: auto;
+  overflow-x: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.table-container {
+  height: 100%;
+  overflow-y: auto;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 13px;
+}
+
+th, td {
+  padding: 8px 16px;
+  text-align: left;
+  border-bottom: 1px solid #333333;
+}
+
+th {
+  position: sticky;
+  top: 0;
+  font-weight: 500;
+  color: #858585;
+  background-color: #252526;
+  z-index: 1;
+}
+
+td {
+  color: #cccccc;
+}
+
+tr:hover td {
+  background-color: #2a2d2e;
+}
+
+tr.selected td {
+  background-color: #37373d;
+}
+
+.empty-state {
+  padding: 24px;
+  text-align: center;
+  color: #666;
+  background-color: #f9f9f9;
+  border-radius: 4px;
+  margin-top: 16px;
+}
+.context-menu {
+  position: fixed;
+  background-color: #252526;
+  border: 1px solid #333333;
+  border-radius: 4px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
+  z-index: 1000;
+  min-width: 150px;
+}
+
+.menu-item {
+  padding: 8px 12px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #cccccc;
+  font-size: 13px;
+  text-align: left;
+}
+
+.menu-item:hover {
+  background-color: #2a2d2e;
+}
+
+.menu-icon {
+  font-size: 16px;
+}
+
+.menu-item.disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.menu-text {
+  font-size: 14px;
+}
+
+.menu-error {
+  padding: 8px 12px;
+  color: #f44336;
+  font-size: 12px;
+  border-top: 1px solid #3c3c3c;
+  max-width: 200px;
+  word-break: break-word;
+}
+
+.empty-state {
+  padding: 24px;
+  text-align: center;
+  color: #666;
+  background-color: #f9f9f9;
+  border-radius: 4px;
+  margin-top: 16px;
+}
+
+.status-active {
+  color: #4caf50;
+}
+
+.status-terminating {
+  color: #f44336;
+}
+
+.status-unknown {
+  color: #9e9e9e;
+}
+
+.status-failed {
+  color: #f44336;
+}
+
+.status-unknown {
+  color: #9e9e9e;
+}

diff --git a/frontend/src/assets/css/MenuDialog.css b/frontend/src/assets/css/MenuDialog.css
line changes: +233/-0
index 0000000..109c07d
--- /dev/null
+++ b/frontend/src/assets/css/MenuDialog.css
@@ -0,0 +1,233 @@
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.7);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.modal-content {
+  background-color: #252526;
+  border-radius: 4px;
+  width: 500px;
+  max-width: 90%;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  border: 1px solid #3c3c3c;
+  color: #cccccc;
+  display: flex;
+  flex-direction: column;
+  max-height: 90vh;
+  overflow: hidden;
+}
+
+.yaml-editor {
+  width: 600px;
+  max-width: 90%;
+}
+
+.modal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 12px;
+  border-bottom: 1px solid #3c3c3c;
+  background-color: #2a2a2a;
+  flex-shrink: 0;
+}
+
+.modal-header h3 {
+  margin: 0;
+  font-size: 16px;
+  font-weight: 500;
+  color: #cccccc;
+}
+
+.close-button {
+  background: none;
+  border: none;
+  font-size: 20px;
+  cursor: pointer;
+  color: #858585;
+  padding: 0;
+  margin: 0;
+  line-height: 1;
+}
+
+.close-button:hover {
+  color: #cccccc;
+}
+
+.modal-body {
+  padding: 16px;
+  flex-grow: 1;
+  overflow: auto;
+}
+
+.modal-footer {
+  padding: 12px 16px;
+  border-top: 1px solid #3c3c3c;
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+  background-color: #2a2a2a;
+  flex-shrink: 0;
+}
+
+.yaml-container {
+  width: 100%;
+  height: 300px;
+  position: relative;
+}
+
+.form-group {
+  margin-bottom: 16px;
+  text-align: left;
+}
+
+.form-group label {
+  display: block;
+  margin-bottom: 6px;
+  font-weight: normal;
+  font-size: 13px;
+  color: #cccccc;
+  text-align: left;
+  width: 100%;
+}
+
+.form-group input {
+  width: 100%;
+  padding: 8px 10px;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  font-size: 13px;
+  background-color: #1e1e1e;
+  color: #cccccc;
+  box-sizing: border-box;
+}
+
+.form-group input:focus {
+  outline: none;
+  border-color: #0e639c;
+}
+
+.namespace-display {
+  padding: 8px 10px;
+  background-color: #1e1e1e;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  font-size: 13px;
+  color: #cccccc;
+}
+
+.error-message {
+  color: #f44336;
+  margin-bottom: 12px;
+  padding: 8px 12px;
+  background-color: rgba(244, 67, 54, 0.1);
+  border-radius: 4px;
+  border-left: 3px solid #f44336;
+  font-size: 13px;
+}
+
+.cancel-button {
+  padding: 6px 12px;
+  background-color: #333333;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 13px;
+  color: #cccccc;
+  min-width: 80px;
+}
+
+.cancel-button:hover {
+  background-color: #3c3c3c;
+}
+
+.update-button {
+  padding: 6px 12px;
+  background-color: #0e639c;
+  
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 13px;
+  color: #cccccc;
+  min-width: 80px;
+}
+
+.update-button:hover {
+  background-color: #3c3c3c;
+}
+
+.update-button:disabled, .cancel-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.create-button {
+  padding: 6px 12px;
+  background-color: #0e639c;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 13px;
+  min-width: 80px;
+}
+
+.create-button:hover {
+  background-color: #1177bb;
+}
+
+.create-button:disabled, .cancel-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+::placeholder {
+  color: #666666;
+  opacity: 1;
+}
+
+.yaml-textarea {
+  width: 100%;
+  height: 100%;
+  padding: 10px;
+  border: 1px solid #3c3c3c;
+  border-radius: 2px;
+  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
+  font-size: 13px;
+  line-height: 1.5;
+  resize: none;
+  background-color: #1e1e1e;
+  color: #cccccc;
+  box-sizing: border-box;
+  overflow: auto;
+}
+
+.yaml-textarea:focus {
+  outline: none;
+  border-color: #0e639c;
+}
+
+.yaml-textarea::placeholder {
+  color: #666666;
+  opacity: 1;
+}
+
+.loading-message {
+  color: #42a5f5;
+  margin-bottom: 12px;
+  padding: 8px 12px;
+  background-color: rgba(66, 165, 245, 0.1);
+  border-radius: 2px;
+  border-left: 3px solid #42a5f5;
+  font-size: 13px;
+  text-align: center;
+}

diff --git a/frontend/src/assets/css/NamespacesList.css b/frontend/src/assets/css/NamespacesList.css
line changes: +122/-0
index 0000000..f0c2e12
--- /dev/null
+++ b/frontend/src/assets/css/NamespacesList.css
@@ -0,0 +1,122 @@
+.namespaces-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.search-bar-container {
+  flex-shrink: 0;
+  width: 100%;
+  background-color: #2a2a2a;
+  border-bottom: 1px solid #444;
+  z-index: 10;
+  box-sizing: border-box; /* Include padding in width calculation */
+  padding: 0; /* Remove any padding that might be causing overflow */
+}
+
+.table-scroll-container {
+  flex: 1;
+  overflow-y: auto;
+  overflow-x: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.no-namespaces {
+  padding: 20px;
+  text-align: center;
+  color: #cccccc;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 13px;
+}
+
+th, td {
+  padding: 8px 16px;
+  text-align: left;
+  border-bottom: 1px solid #333333;
+}
+
+th {
+  position: sticky;
+  top: 0;
+  font-weight: 500;
+  color: #858585;
+  background-color: #252526;
+  z-index: 1;
+}
+
+td {
+  color: #cccccc;
+}
+
+tr:hover td {
+  background-color: #2a2d2e;
+}
+
+tr.selected td {
+  background-color: #37373d;
+}
+
+.status-active {
+  color: #4caf50;
+}
+
+.status-terminating {
+  color: #f44336;
+}
+
+.status-unknown {
+  color: #9e9e9e;
+}
+
+.status-failed {
+  color: #f44336;
+}
+
+.status-unknown {
+  color: #9e9e9e;
+}
+
+.context-menu {
+  position: fixed;
+  background-color: #252526;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  z-index: 1000;
+  min-width: 120px;
+}
+
+.menu-item {
+  padding: 8px 12px;
+  cursor: pointer;
+  color: #cccccc;
+  font-size: 13px;
+  text-align: left;
+}
+
+.menu-item:hover {
+  background-color: #2a2d2e;
+}
+
+.menu-item.disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.menu-error {
+  padding: 8px 12px;
+  color: #f44336;
+  font-size: 12px;
+  border-top: 1px solid #3c3c3c;
+  max-width: 200px;
+  word-break: break-word;
+}

diff --git a/frontend/src/assets/css/NavigationPane.css b/frontend/src/assets/css/NavigationPane.css
line changes: +57/-0
index 0000000..a731282
--- /dev/null
+++ b/frontend/src/assets/css/NavigationPane.css
@@ -0,0 +1,57 @@
+.nav-pane {
+  width: 250px;
+  background-color: #252526;
+  height: 100%;
+  overflow-y: auto;
+}
+
+.nav-section {
+  margin-bottom: 1rem;
+}
+
+.section-header {
+  padding: 8px 16px;
+  background-color: #333333;
+  height: 55px; /* Increased to match search container + divider */
+  display: flex;
+  align-items: center;
+  box-sizing: border-box;
+  border-bottom: 1px solid #444; /* Added divider to match search container */
+}
+
+.section-title {
+  font-size: 12px;
+  font-weight: 500;
+  color: #858585;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+}
+
+.section-content {
+  padding: 8px 0;
+}
+
+.nav-item {
+  padding: 8px 16px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #cccccc;
+}
+
+.nav-item:hover {
+  background-color: #2a2d2e;
+}
+
+.nav-item.active {
+  background-color: #37373d;
+  color: #ffffff;
+}
+
+.nav-item i {
+  width: 16px;
+  text-align: center;
+  font-size: 14px;
+}
+

diff --git a/frontend/src/assets/css/PersistentVolumes.css b/frontend/src/assets/css/PersistentVolumes.css
line changes: +211/-0
index 0000000..50e6f08
--- /dev/null
+++ b/frontend/src/assets/css/PersistentVolumes.css
@@ -0,0 +1,211 @@
+/*
+ * NOTE(rene): generated from LLM inline with vuejs. I've split these out into
+ * dedicated PersistentVolume.css file but will pick and choose resources to
+ * copy back into CreateResource.css.
+ */
+.guided-creator {
+  max-width: 800px;
+  width: 95vw;
+  max-height: 90vh;
+  overflow-y: auto;
+}
+
+.volume-type-selector {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+  gap: 1rem;
+  margin-bottom: 1rem;
+}
+
+.volume-type-option {
+  border: 2px solid #444;
+  border-radius: 8px;
+  padding: 1rem;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  background-color: #2d2d2d;
+}
+
+.volume-type-option:hover {
+  border-color: #007acc;
+  background-color: #333;
+}
+
+.volume-type-option.selected {
+  border-color: #007acc;
+  background-color: #1a3a5c;
+}
+
+.type-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 0.5rem;
+}
+
+.type-name {
+  font-weight: 600;
+  color: #ffffff;
+}
+
+.type-badge {
+  padding: 0.25rem 0.5rem;
+  border-radius: 12px;
+  font-size: 0.75rem;
+  font-weight: 500;
+  text-transform: uppercase;
+}
+
+.type-badge.local {
+  background-color: #28a745;
+  color: white;
+}
+
+.type-badge.network {
+  background-color: #007acc;
+  color: white;
+}
+
+.type-badge.cloud {
+  background-color: #6f42c1;
+  color: white;
+}
+
+.type-description {
+  color: #ccc;
+  font-size: 0.9rem;
+  margin: 0;
+  line-height: 1.4;
+}
+
+.volume-config {
+  background-color: #333;
+  border: 1px solid #444;
+  border-radius: 6px;
+  padding: 1rem;
+}
+
+.storage-input-group {
+  display: flex;
+  gap: 0.5rem;
+}
+
+.storage-input-group input {
+  flex: 1;
+}
+
+.storage-input-group select {
+  width: 80px;
+  padding: 0.5rem;
+  border: 1px solid #444;
+  border-radius: 4px;
+  background-color: #2d2d2d;
+  color: #ffffff;
+}
+
+.subsection {
+  margin-bottom: 1.5rem;
+}
+
+.subsection:last-child {
+  margin-bottom: 0;
+}
+
+.subsection h5 {
+  margin: 0 0 0.75rem 0;
+  color: #ffffff;
+  font-size: 1rem;
+}
+
+.checkbox-group,
+.radio-group {
+  display: flex;
+  flex-direction: column;
+  gap: 0.75rem;
+}
+
+.checkbox-label,
+.radio-label {
+  display: flex;
+  flex-direction: column;
+  gap: 0.25rem;
+  cursor: pointer;
+}
+
+.checkbox-label input,
+.radio-label input {
+  width: auto;
+  margin-right: 0.5rem;
+}
+
+.label-row {
+  display: flex;
+  gap: 0.5rem;
+  align-items: flex-start;
+  margin-bottom: 0.5rem;
+}
+
+.key-field,
+.value-field {
+  flex: 1;
+  margin-bottom: 0;
+}
+
+.remove-button {
+  background-color: #dc3545;
+  border: 1px solid #dc3545;
+  color: white;
+  border-radius: 4px;
+  width: 30px;
+  height: 30px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: 1.5rem;
+}
+
+.remove-button:hover:not(:disabled) {
+  background-color: #c82333;
+}
+
+.remove-button:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+
+.add-button {
+  background-color: #28a745;
+  border: 1px solid #28a745;
+  color: white;
+  padding: 0.5rem 1rem;
+  border-radius: 4px;
+  cursor: pointer;
+  margin-top: 0.5rem;
+}
+
+.add-button:hover:not(:disabled) {
+  background-color: #218838;
+}
+
+.add-button:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+
+/* Responsive design */
+@media (max-width: 768px) {
+  .volume-type-selector {
+    grid-template-columns: 1fr;
+  }
+
+  .label-row {
+    flex-direction: column;
+    gap: 0.25rem;
+  }
+
+  .remove-button {
+    margin-top: 0.5rem;
+    align-self: flex-start;
+  }
+}

diff --git a/frontend/src/assets/css/PodsList.css b/frontend/src/assets/css/PodsList.css
line changes: +127/-0
index 0000000..4f0b937
--- /dev/null
+++ b/frontend/src/assets/css/PodsList.css
@@ -0,0 +1,127 @@
+.pods-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  width: 100%; /* Ensure container takes full width */
+  box-sizing: border-box; /* Include padding in width calculation */
+}
+
+.search-bar-container {
+  flex-shrink: 0;
+  width: 100%;
+  background-color: #2a2a2a;
+  border-bottom: 1px solid #444;
+  z-index: 10;
+  box-sizing: border-box; /* Include padding in width calculation */
+  padding: 0; /* Remove any padding that might be causing overflow */
+}
+
+.table-scroll-container {
+  flex: 1;
+  overflow-y: auto;
+  overflow-x: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.no-pods {
+  padding: 20px;
+  text-align: center;
+  color: #cccccc;
+}
+
+.table-container {
+  height: 100%;
+  overflow-y: auto;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 13px;
+}
+
+th, td {
+  padding: 8px 16px;
+  text-align: left;
+  border-bottom: 1px solid #333333;
+}
+
+th {
+  position: sticky;
+  top: 0;
+  font-weight: 500;
+  color: #858585;
+  background-color: #252526;
+  z-index: 1;
+}
+
+td {
+  color: #cccccc;
+}
+
+tr:hover td {
+  background-color: #2a2d2e;
+}
+
+tr.selected td {
+  background-color: #37373d;
+}
+
+.status-running {
+  color: #4caf50;
+}
+
+.status-pending {
+  color: #ff9800;
+}
+
+.status-succeeded {
+  color: #2196f3;
+}
+
+.status-failed {
+  color: #f44336;
+}
+
+.status-unknown {
+  color: #9e9e9e;
+}
+
+.context-menu {
+  position: fixed;
+  background-color: #252526;
+  border: 1px solid #3c3c3c;
+  border-radius: 4px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
+  z-index: 1000;
+  min-width: 120px;
+}
+
+.menu-item {
+  padding: 8px 12px;
+  cursor: pointer;
+  color: #cccccc;
+  font-size: 13px;
+  text-align: left;
+}
+
+.menu-item:hover {
+  background-color: #2a2d2e;
+}
+
+.menu-item.disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.menu-error {
+  padding: 8px 12px;
+  color: #f44336;
+  font-size: 12px;
+  border-top: 1px solid #3c3c3c;
+  max-width: 200px;
+  word-break: break-word;
+}

diff --git a/frontend/src/assets/css/SearchBar.css b/frontend/src/assets/css/SearchBar.css
line changes: +61/-0
index 0000000..5440e3f
--- /dev/null
+++ b/frontend/src/assets/css/SearchBar.css
@@ -0,0 +1,61 @@
+/* SearchBar.css */
+/* Update in SearchBar.css */
+.search-container {
+  width: 100%;
+  padding: 8px;
+  background-color: #2a2a2a;
+  box-sizing: border-box; /* Include padding in width calculation */
+  max-width: 100%; /* Ensure it doesn't exceed parent width */
+  overflow: hidden; /* Prevent overflow */
+}
+
+.search-input-wrapper {
+  position: relative;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  box-sizing: border-box;
+}
+
+.search-input {
+  width: 100%;
+  padding: 8px 32px 8px 12px;
+  font-size: 14px;
+  border: 1px solid #444;
+  border-radius: 4px;
+  outline: none;
+  transition: border-color 0.2s, box-shadow 0.2s;
+  background-color: #333;
+  color: #e0e0e0;
+  box-sizing: border-box; /* Include padding in width calculation */
+}
+
+.search-input:focus {
+  border-color: #4a90e2; /* Blue highlight when focused */
+  box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
+}
+
+.search-input::placeholder {
+  color: #888; /* Lighter placeholder text */
+}
+
+.clear-button {
+  position: absolute;
+  right: 8px;
+  background: none;
+  border: none;
+  font-size: 18px;
+  color: #888;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  height: 24px;
+  border-radius: 50%;
+}
+
+.clear-button:hover {
+  background-color: rgba(255, 255, 255, 0.1);
+  color: #ccc;
+}

diff --git a/frontend/src/assets/css/StatefulSetsList.css b/frontend/src/assets/css/StatefulSetsList.css
line changes: +104/-0
index 0000000..aad6c32
--- /dev/null
+++ b/frontend/src/assets/css/StatefulSetsList.css
@@ -0,0 +1,104 @@
+.statefulsets-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.search-bar-container {
+  flex-shrink: 0;
+  width: 100%;
+  background-color: #2a2a2a;
+  border-bottom: 1px solid #444;
+  z-index: 10;
+  box-sizing: border-box;
+  padding: 0;
+}
+
+.table-scroll-container {
+  flex: 1;
+  overflow-y: auto;
+  overflow-x: hidden;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.table-container {
+  height: 100%;
+  overflow-y: auto;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 13px;
+}
+
+th, td {
+  padding: 8px 16px;
+  text-align: left;
+  border-bottom: 1px solid #333333;
+}
+
+th {
+  position: sticky;
+  top: 0;
+  font-weight: 500;
+  color: #858585;
+  background-color: #252526;
+  z-index: 1;
+}
+
+td {
+  color: #cccccc;
+}
+
+tr:hover td {
+  background-color: #2a2d2e;
+}
+
+tr.selected td {
+  background-color: #37373d;
+}
+
+.context-menu {
+  position: fixed;
+  background-color: #252526;
+  border: 1px solid #333333;
+  border-radius: 4px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
+  z-index: 1000;
+  min-width: 150px;
+}
+
+.menu-item {
+  padding: 8px 12px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.menu-item:hover {
+  background-color: #2a2d2e;
+}
+
+.menu-icon {
+  font-size: 16px;
+}
+
+.menu-text {
+  font-size: 14px;
+}
+
+.empty-state {
+  padding: 24px;
+  text-align: center;
+  color: #666;
+  background-color: #f9f9f9;
+  border-radius: 4px;
+  margin-top: 16px;
+}

diff --git a/frontend/src/assets/fonts/OFL.txt b/frontend/src/assets/fonts/OFL.txt
line changes: +93/-0
index 0000000..9cac04c
--- /dev/null
+++ b/frontend/src/assets/fonts/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com),
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.

diff --git a/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 b/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2
line changes: +0/-0
index 0000000..2f9cc59
--- /dev/null
+++ b/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2

diff --git a/frontend/src/assets/images/logo-universal.png b/frontend/src/assets/images/logo-universal.png
line changes: +0/-0
index 0000000..e9913c1
--- /dev/null
+++ b/frontend/src/assets/images/logo-universal.png

diff --git a/frontend/src/components/BottomTabs.vue b/frontend/src/components/BottomTabs.vue
line changes: +587/-0
index 0000000..75e9c83
--- /dev/null
+++ b/frontend/src/components/BottomTabs.vue
@@ -0,0 +1,587 @@
+<script lang="ts">
+import { defineComponent, PropType, ref, watch, nextTick } from 'vue';
+import { KubernetesCluster } from '../types/kubernetes';
+import { formatResourceDescription } from '../lib/lib';
+import { PodTerminal, TerminalOptions } from '../lib/terminal';
+import { logsService } from '../lib/logs';
+import { type KubernetesResourceKind, getResourceKind, isPod, isDeployment, isStatefulSet, isConfigMap } from '../lib/resources';
+import { kubernetesService } from '../lib/kubernetes';
+
+export default defineComponent({
+  name: 'BottomTabs',
+
+  props: {
+    selectedResource: {
+      type: Object as PropType<any>,
+      required: true
+    },
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: true
+    },
+    selectedTab: {
+      type: String as PropType<'describe' | 'logs' | 'terminal'>,
+      required: true
+    }
+  },
+
+  emits: ['tab-changed'],
+
+  data() {
+    return {
+      logs: '',
+      description: '',
+      loading: false,
+      error: '',
+      isPod: false,
+      isDeployment: false,
+      isStatefulSet: false,
+      isConfigMap: false,
+      tailLines: 100,
+      selectedContainer: '',
+      containers: [] as string[],
+      panelHeight: 300, // Default height in pixels
+      isResizing: false,
+      startY: 0,
+      startHeight: 0,
+      // Terminal-related properties
+      podTerminal: null as PodTerminal | null,
+      terminalError: '',
+      // Reference to the logs container
+      logsContainerRef: null as HTMLElement | null,
+      resourceKind: null as KubernetesResourceKind,
+    };
+  },
+
+  watch: {
+    selectedResource: {
+      immediate: true,
+      handler(newResource, oldResource) {
+        // Only reload if the resource actually changed
+        if (JSON.stringify(newResource?.metadata?.uid) !== JSON.stringify(oldResource?.metadata?.uid)) {
+          this.getResourceKind();
+          this.determineResourceType();
+          this.extractContainers();
+
+          // If we're on the logs tab, we need to reconnect
+          if (this.selectedTab === 'logs') {
+            this.reconnectLogsWebSocket();
+          } else if (this.selectedTab === 'describe') {
+            this.generateDescription();
+          } else if (this.selectedTab === 'terminal') {
+            // If we're on the terminal tab but the new resource isn't a pod,
+            // switch to the describe tab
+            if (!this.isPod) {
+              this.$emit('tab-changed', 'describe');
+            } else {
+              // Otherwise, initialize the terminal for the new pod
+              this.initTerminal();
+            }
+          }
+        }
+      }
+    },
+    selectedTab: {
+      handler(newTab, oldTab) {
+        if (newTab === 'logs' && oldTab !== 'logs') {
+          this.reconnectLogsWebSocket();
+        } else if (newTab !== 'logs' && oldTab === 'logs') {
+          this.closeLogsWebSocket();
+          // If switching to describe tab, regenerate the description
+          if (newTab === 'describe') {
+            this.generateDescription();
+          }
+        } else if (newTab === 'describe' && oldTab !== 'describe') {
+          // Always regenerate description when switching to describe tab
+          this.generateDescription();
+        } else if (newTab === 'terminal' && oldTab !== 'terminal') {
+          // Initialize terminal when switching to terminal tab
+          this.$nextTick(() => {
+            this.initTerminal();
+          });
+        } else if (newTab !== 'terminal' && oldTab === 'terminal') {
+          // Dispose terminal when switching away from terminal tab
+          this.disposePodTerminal();
+        }
+      }
+    },
+    selectedContainer() {
+      if (this.selectedTab === 'logs') {
+        this.reconnectLogsWebSocket();
+      } else if (this.selectedTab === 'terminal' && this.podTerminal) {
+        // If the terminal tab is active and container changes, refresh the terminal
+        this.refreshTerminal();
+      }
+    },
+    // Watch logs changes to scroll to bottom
+    logs() {
+      if (this.selectedTab === 'logs') {
+        this.scrollToBottom();
+      }
+    }
+  },
+
+  created() {
+    this.getResourceKind();
+    this.determineResourceType();
+    this.extractContainers();
+  },
+
+  mounted() {
+    // Add event listeners for mouse events to handle resizing
+    document.addEventListener('mousemove', this.onMouseMove);
+    document.addEventListener('mouseup', this.onMouseUp);
+
+    // Try to load saved height from localStorage
+    const savedHeight = localStorage.getItem('bottomTabsHeight');
+    if (savedHeight) {
+      this.panelHeight = parseInt(savedHeight, 10);
+    }
+  },
+
+  beforeUnmount() {
+    this.closeLogsWebSocket();
+    this.disposePodTerminal();
+
+    // Remove event listeners
+    document.removeEventListener('mousemove', this.onMouseMove);
+    document.removeEventListener('mouseup', this.onMouseUp);
+  },
+
+  methods: {
+    displayTerminal(): boolean {
+      if (!this.isPod) {
+        return false;
+      }
+      return true;
+    },
+    displayLogs(): boolean {
+      if (this.isDeployment) {
+        return true;
+      } else if (this.isPod) {
+        return true;
+      } else if (this.isStatefulSet) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    getResourceKind(): void {
+      if (!this.selectedResource) {
+        this.resourceKind = null;
+        return;
+      }
+      this.resourceKind = getResourceKind(this.selectedResource);
+    },
+    determineResourceType() {
+      // Determine if the selected resource is a pod or deployment
+      if (!this.selectedResource) {
+        this.isPod = false;
+        this.isDeployment = false;
+        this.isStatefulSet = false;
+        this.isConfigMap = false;
+        return;
+      }
+
+      this.isPod = isPod(this.selectedResource);
+      this.isDeployment = isDeployment(this.selectedResource);
+      this.isStatefulSet = isStatefulSet(this.selectedResource);
+      this.isConfigMap = isConfigMap(this.selectedResource);
+    },
+
+    extractContainers() {
+      // Use the logs service to extract containers
+      this.containers = logsService.extractContainers(this.selectedResource);
+
+      // Only set selectedContainer if containers exist and it's not already set to a valid container
+      if (this.containers.length > 0) {
+        if (!this.containers.includes(this.selectedContainer)) {
+          this.selectedContainer = this.containers[0];
+        }
+      } else {
+        this.selectedContainer = '';
+      }
+    },
+
+    async reconnectLogsWebSocket() {
+      if (!this.selectedResource || !this.selectedCluster) {
+        this.logs = 'No resource selected';
+        return;
+      }
+
+      // Create options object for logs service
+      const options = {
+        selectedResource: this.selectedResource,
+        selectedCluster: this.selectedCluster,
+        selectedContainer: this.selectedContainer,
+        tailLines: this.tailLines
+      };
+
+      // Use the logs service to reconnect with callbacks
+      await logsService.reconnectLogsWebSocket(options, {
+        onMessage: (newLogs) => {
+          // Append new log lines
+          this.logs += newLogs;
+        },
+        onError: (error) => {
+          // Only show error if we're still on the logs tab
+          if (this.selectedTab === 'logs') {
+            this.error = error;
+          }
+        },
+        onOpen: () => {
+          this.logs = '';
+          this.error = '';
+        }
+      });
+    },
+
+    closeLogsWebSocket() {
+      // Use the logs service to close the WebSocket
+      return logsService.closeLogsWebSocket();
+    },
+
+    async generateDescription(): void {
+      if (!this.selectedResource) {
+        this.description = 'No resource selected';
+        return;
+      }
+
+      if (!this.selectedCluster) {
+        this.description = 'No cluster selected';
+        return;
+      }
+
+      this.loading = true;
+      this.error = '';
+
+
+      const context = this.selectedCluster.contextName;
+      const name = this.selectedResource.metadata.name;
+
+      let namespace: string | undefined;
+      try {
+        namespace = this.selectedResource.metadata.namespace;
+        if (!namespace) {
+          namespace = this.selectedResource.metadata.name;
+        }
+        if (!namespace) {
+          throw new Error("Unable to get namespace for this resource");
+        }
+      } catch (error) {
+        this.error = error;
+        this.loading = false;
+        return;
+      }
+
+      try {
+        let response;
+        switch (this.resourceKind) {
+          case 'Pod':
+            response = await kubernetesService.describePod(context, namespace, name);
+            break;
+          case 'ConfigMap':
+            response = await kubernetesService.describeConfigMap(context, namespace, name);
+            break;
+          case 'Deployment':
+            response = await kubernetesService.describeDeployment(context, namespace, name);
+            break;
+          case 'StatefulSet':
+            response = await kubernetesService.describeStatefulSet(context, namespace, name);
+            break;
+          case 'Ingress':
+            response = await kubernetesService.describeIngress(context, namespace, name);
+            break;
+          case 'Namespace':
+            response = await kubernetesService.describeNamespace(context, namespace);
+            break;
+          case 'Secret':
+            response = await kubernetesService.describeSecret(context, namespace, name);
+            break;
+          case 'PersistentVolumeClaim':
+            response = await kubernetesService.describePersistentVolumeClaim(context, namespace, name);
+            break;
+          case 'PersistentVolume':
+            response = await kubernetesService.describePersistentVolume(context, namespace, name);
+            break;
+          default:
+            throw new Error(this.resourceKind + " is an unsupported resource type");
+        }
+
+        if (response.success && response.data) {
+          this.description = response.data;
+        } else {
+          throw new Error(response.msg || "failed to get description");
+        }
+      } catch (error) {
+        console.error('Error generating description:', error);
+        this.error = `Error generating description: ${error}`;
+        return;
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    changeTab(tab: 'describe' | 'logs' | 'terminal') {
+      // Clear any previous errors when changing tabs
+      this.error = '';
+
+      // If switching to describe tab, make sure we regenerate the description
+      if (tab === 'describe' && this.selectedTab !== 'describe') {
+        this.$nextTick(() => {
+          this.generateDescription();
+        });
+      }
+
+      this.$emit('tab-changed', tab);
+    },
+
+    updateTailLines(event: Event) {
+      const target = event.target as HTMLSelectElement;
+      this.tailLines = parseInt(target.value, 10);
+
+      if (this.selectedTab === 'logs') {
+        this.reconnectLogsWebSocket();
+      }
+    },
+
+    clearLogs() {
+      this.logs = '';
+      logsService.clearLogs();
+    },
+
+    // Scroll logs to bottom - using a direct DOM approach
+    scrollToBottom() {
+      // Use requestAnimationFrame to ensure we're scrolling after the browser has rendered
+      requestAnimationFrame(() => {
+        const logsContainer = document.querySelector('.logs-container');
+        if (logsContainer) {
+          logsContainer.scrollTop = logsContainer.scrollHeight;
+
+          // Double-check with a timeout to ensure it really scrolled
+          setTimeout(() => {
+            if (logsContainer) {
+              logsContainer.scrollTop = logsContainer.scrollHeight;
+            }
+          }, 100);
+        }
+      });
+    },
+
+    // Resizing methods
+    startResize(event: MouseEvent) {
+      this.isResizing = true;
+      this.startY = event.clientY;
+      this.startHeight = this.panelHeight;
+
+      // Prevent text selection during resize
+      event.preventDefault();
+    },
+
+    onMouseMove(event: MouseEvent) {
+      if (!this.isResizing) return;
+
+      // Calculate new height (resize from top)
+      const deltaY = this.startY - event.clientY;
+      const newHeight = Math.max(100, this.startHeight + deltaY); // Minimum height of 100px
+
+      this.panelHeight = newHeight;
+    },
+
+    onMouseUp() {
+      if (this.isResizing) {
+        this.isResizing = false;
+
+        // Save the height to localStorage
+        localStorage.setItem('bottomTabsHeight', this.panelHeight.toString());
+      }
+    },
+
+    // Terminal methods
+    initTerminal() {
+      if (!this.isPod || !this.selectedResource || !this.selectedCluster) {
+        this.terminalError = 'Terminal is only available for pods';
+        return;
+      }
+
+      this.terminalError = '';
+
+      // Dispose any existing terminal
+      this.disposePodTerminal();
+
+      try {
+        // Create terminal options
+        const options: TerminalOptions = {
+          contextName: this.selectedCluster.contextName,
+          namespace: this.selectedResource.metadata.namespace,
+          podName: this.selectedResource.metadata.name,
+          containerName: this.selectedContainer || ''
+        };
+
+        // Create new terminal instance
+        this.podTerminal = new PodTerminal(options);
+
+        // Initialize terminal
+        this.$nextTick(async () => {
+          try {
+            await this.podTerminal?.initialize('terminal-container');
+          } catch (error) {
+            console.error('Error initializing pod terminal:', error);
+            this.terminalError = `Error initializing terminal: ${error}`;
+            this.disposePodTerminal();
+          }
+        });
+      } catch (error) {
+        console.error('Error setting up pod terminal:', error);
+        this.terminalError = `Error setting up terminal: ${error}`;
+      }
+    },
+
+    disposePodTerminal() {
+      if (this.podTerminal) {
+        this.podTerminal.dispose();
+        this.podTerminal = null;
+      }
+    },
+
+    refreshTerminal() {
+      if (this.podTerminal) {
+        this.terminalError = '';
+
+        try {
+          // Update options if container changed
+          this.podTerminal.updateOptions({
+            containerName: this.selectedContainer || ''
+          });
+
+          // Refresh the terminal
+          this.podTerminal.refresh();
+        } catch (error) {
+          console.error('Error refreshing terminal:', error);
+          this.terminalError = `Error refreshing terminal: ${error}`;
+        }
+      } else {
+        // Initialize a new terminal if none exists
+        this.initTerminal();
+      }
+    }
+  }
+});
+</script>
+
+<template>
+  <div class="bottom-tabs" :style="{ height: `${panelHeight}px` }">
+    <div class="resize-handle" @mousedown="startResize"></div>
+
+    <div class="tabs-header">
+      <div class="tabs">
+        <button 
+          :class="{ active: selectedTab === 'describe' }" 
+          @click="changeTab('describe')"
+        >
+          Describe
+        </button>
+        <button 
+          v-if="displayLogs()"
+          :class="{ active: selectedTab === 'logs' }" 
+          @click="changeTab('logs')"
+        >
+          Logs
+        </button>
+        <button 
+          v-if="displayTerminal()"
+          :class="{ active: selectedTab === 'terminal' }" 
+          @click="changeTab('terminal')"
+        >
+          Terminal
+        </button>
+      </div>
+
+      <!-- Logs tab actions -->
+      <div class="tabs-actions" v-if="selectedTab === 'logs'">
+        <select 
+          v-if="containers.length > 1"
+          v-model="selectedContainer"
+          title="Select container"
+        >
+          <option v-for="container in containers" :key="container" :value="container">
+            {{ container }}
+          </option>
+        </select>
+
+        <select 
+          v-model="tailLines" 
+          @change="updateTailLines"
+          title="Number of lines to show"
+        >
+          <option value="10">10 lines</option>
+          <option value="50">50 lines</option>
+          <option value="100">100 lines</option>
+          <option value="500">500 lines</option>
+          <option value="1000">1000 lines</option>
+        </select>
+
+        <button 
+          @click="clearLogs"
+          title="Clear logs"
+        >
+          Clear
+        </button>
+      </div>
+
+      <!-- Terminal tab actions - positioned in the same place as logs actions -->
+      <div class="tabs-actions" v-if="selectedTab === 'terminal'">
+        <select 
+          v-if="containers.length > 1"
+          v-model="selectedContainer"
+          @change="refreshTerminal"
+          title="Select container"
+        >
+          <option v-for="container in containers" :key="container" :value="container">
+            {{ container }}
+          </option>
+        </select>
+
+        <button 
+          @click="refreshTerminal"
+          title="Refresh terminal"
+        >
+          Refresh
+        </button>
+      </div>
+    </div>
+
+    <div class="tab-content">
+      <div v-if="loading" class="loading">
+        Loading...
+      </div>
+
+      <div v-else-if="error" class="error">
+        {{ error }}
+      </div>
+
+      <div v-else-if="selectedTab === 'describe'" class="describe-container">
+        <pre class="left-aligned">{{ description }}</pre>
+      </div>
+
+      <div 
+        v-else-if="selectedTab === 'logs'" 
+        class="logs-container"
+      >
+        <pre class="left-aligned terminal-style">{{ logs }}</pre>
+      </div>
+
+      <div v-else-if="selectedTab === 'terminal'" class="terminal-container">
+        <div v-if="!isPod" class="error">
+          Terminal is only available for pods
+        </div>
+        <div v-else-if="terminalError" class="error">
+          {{ terminalError }}
+        </div>
+        <div v-else id="terminal-container" class="left-aligned terminal-style"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/BottomTabs.css" scoped></style>

diff --git a/frontend/src/components/ClusterList.vue b/frontend/src/components/ClusterList.vue
line changes: +76/-0
index 0000000..0fbc88f
--- /dev/null
+++ b/frontend/src/components/ClusterList.vue
@@ -0,0 +1,76 @@
+<script lang="ts">
+import { defineComponent, PropType } from 'vue';
+import { KubernetesCluster } from '../types/kubernetes';
+
+export default defineComponent({
+  name: 'ClusterList',
+
+  props: {
+    clusters: {
+      type: Array as PropType<KubernetesCluster[]>,
+      required: true,
+      default: () => []
+    },
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: false,
+      default: null
+    }
+  },
+
+  methods: {
+    isSelected(cluster: KubernetesCluster): boolean {
+      return this.selectedCluster?.contextName === cluster.contextName;
+    },
+
+    selectCluster(cluster: KubernetesCluster) {
+      this.$emit('select-cluster', cluster);
+    }
+  }
+});
+</script>
+
+<template>
+  <div class="cluster-list">
+    <div 
+      v-for="cluster in clusters" 
+      :key="cluster.server"
+      :class="['cluster-item', { active: isSelected(cluster) }]"
+      @click="selectCluster(cluster)"
+    >
+      <i class="fas fa-server"></i>
+      {{ cluster.contextName }}
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.cluster-list {
+  padding: 8px 0;
+}
+
+.cluster-item {
+  padding: 8px 16px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #cccccc;
+}
+
+.cluster-item:hover {
+  background-color: #2a2d2e;
+}
+
+.cluster-item.active {
+  background-color: #37373d;
+  color: #ffffff;
+}
+
+.cluster-item i {
+  width: 16px;
+  text-align: center;
+  font-size: 14px;
+}
+</style>
+

diff --git a/frontend/src/components/ConfigMapsList.vue b/frontend/src/components/ConfigMapsList.vue
line changes: +129/-0
index 0000000..7a4cbe0
--- /dev/null
+++ b/frontend/src/components/ConfigMapsList.vue
@@ -0,0 +1,129 @@
+<script lang="ts">
+import { defineComponent, ref, computed, PropType, onBeforeUnmount } from 'vue';
+import { KubernetesCluster, KubernetesConfigMap } from '../types/kubernetes';
+import SearchBar from './SearchBar.vue';
+import { formatAge } from '../lib/format';
+
+export default defineComponent({
+    name: 'ConfigMapsList',
+    components: {
+        SearchBar
+    },
+    props: {
+        selectedCluster: {
+            type: Object as PropType<KubernetesCluster>,
+            required: false,
+            default: null
+        },
+        configMaps: {
+            type: Array as PropType<KubernetesConfigMap[]>,
+            required: false,
+            default: () => []
+        }
+    },
+    emits: ['configmap-selected'],
+    data() {
+        return {
+            selectedConfigMap: null as KubernetesConfigMap | null
+        };
+    },
+    setup(props, { emit }) {
+        const searchQuery = ref('');
+        const filteredConfigMaps = computed(() => {
+            if (!searchQuery.value) {
+                return props.configMaps;
+            }
+
+            const query = searchQuery.value.toLowerCase();
+
+            const nameSpecificMatch = query.match(/^name:(.+)$/);
+            if (nameSpecificMatch) {
+                const nameQuery = nameSpecificMatch[1].trim();
+                return props.configMaps.filter(configMap => {
+                    const name = configMap.metadata.name.toLowerCase();
+                    return name.includes(nameQuery);
+                });
+            }
+
+            const namespaceSpecificMatch = query.match(/^namespace:(.+)$/);
+            if (namespaceSpecificMatch) {
+                const namespaceQuery = namespaceSpecificMatch[1].trim();
+                return props.configMaps.filter(configMap => {
+                    const namespace = configMap.metadata.namespace.toLowerCase();
+                    return namespace.includes(namespaceQuery);
+                });
+            }
+
+            return props.configMaps.filter(configMap => {
+                const name = configMap.metadata.name.toLowerCase();
+                return name.includes(query);
+            });
+        });
+
+        return {
+            searchQuery,
+            filteredConfigMaps,
+            formatAge
+        };
+    },
+    methods: {
+      isSelected(configMap: KubernetesConfigMap): boolean {
+        if (!this.selectedConfigMap) return false;
+        return this.selectedConfigMap.metadata.name === configMap.metadata.name &&
+          this.selectedConfigMap.metadata.namespace === configMap.metadata.namespace &&
+          this.selectedConfigMap.metadata.uid === configMap.metadata.uid;
+      },
+      selectConfigMap(configMap: KubernetesConfigMap): void {
+        const configMapToEmit = { ...configMap }
+        if (!configMapToEmit.kind) {
+          configMapToEmit.kind = 'ConfigMap';
+        }
+        this.selectedConfigMap = configMapToEmit;
+        this.$emit('configmap-selected', configMapToEmit);
+      },
+      getUniqueKey(configMap: KubernetesConfigMap): string {
+          const namespace = configMap.metadata.namespace || 'default';
+          let key;
+          key = namespace + '-' + configMap.metadata.name + '-' + (configMap.metadata.uid || '');
+          return key;
+      }
+    },
+});
+</script>
+
+<template>
+  <div class="configmaps-container">
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search configmaps..."
+      />
+    </div>
+    <div class="table-scroll-container">
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Namespace</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="configMap in filteredConfigMaps" 
+            :key="getUniqueKey(configMap)"
+            :class="{ selected: isSelected(configMap) }"
+            @click="selectConfigMap(configMap)"
+          >
+            <td>{{ configMap.metadata.name }}</td>
+            <td>{{ configMap.metadata.namespace || 'default' }}</td>
+            <td>{{ formatAge(configMap.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/ConfigMapsList.css" scoped></style>

diff --git a/frontend/src/components/CreatePodGuided.vue b/frontend/src/components/CreatePodGuided.vue
line changes: +186/-0
index 0000000..7dc5d3a
--- /dev/null
+++ b/frontend/src/components/CreatePodGuided.vue
@@ -0,0 +1,186 @@
+<!-- CreatePodGuided.vue -->
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+
+export default defineComponent({
+  name: 'CreatePodGuided',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true
+    },
+    namespace: {
+      type: String,
+      required: true
+    }
+  },
+
+  emits: ['close', 'create-pod'],
+
+  setup(props, { emit }) {
+    const podName = ref('');
+    const podImage = ref('');
+    const namespace = ref('');
+    const command = ref('');
+    const isSubmitting = ref(false);
+    const error = ref('');
+
+    const resetForm = () => {
+      podName.value = '';
+      podImage.value = '';
+      namespace.value = '';
+      command.value = '';
+      error.value = '';
+      isSubmitting.value = false;
+    };
+
+    const closeModal = () => {
+      resetForm();
+      emit('close');
+    };
+
+    const createPod = () => {
+      // Validate form
+      if (!podName.value.trim()) {
+        error.value = 'Pod name is required';
+        return;
+      }
+
+      if (!podImage.value.trim()) {
+        error.value = 'Container image is required';
+        return;
+      }
+
+      if (!namespace.value.trim()) {
+        error.value = 'Namespace is required';
+        return;
+      }
+
+      isSubmitting.value = true;
+
+      const container = {
+        name: 'container-1',
+        image: podImage.value.trim()
+      }
+
+      if (command.value.trim()) {
+        // NOTE(rene): pass command as string within pod definition. This will
+        // be handled by go and transformed into the []string{} which is the
+        // type that corev1.Pod expects.
+        container.command = command.value.trim();
+      }
+
+      let opts = {
+        podDefinition: {
+          apiVersion: 'v1',
+          kind: 'Pod',
+          metadata: {
+            name: podName.value.trim(),
+            namespace: namespace.value.trim()
+          },
+          spec: {
+            containers: [container]
+          }
+        },
+        isYaml: false
+      }
+      emit('create-pod', opts);
+
+      // Reset and close form
+      resetForm();
+      closeModal();
+    };
+
+    return {
+      podName,
+      podImage,
+      namespace,
+      command,
+      isSubmitting,
+      error,
+      closeModal,
+      createPod
+    };
+  }
+});
+</script>
+
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content" @click.stop>
+      <div class="modal-header">
+        <h3>Create Pod</h3>
+        <button class="close-button" @click="closeModal">&times;</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div class="form-group">
+          <label for="pod-name">Pod Name:</label>
+          <input 
+            id="pod-name" 
+            v-model="podName" 
+            type="text" 
+            placeholder="test-pod-1"
+            :disabled="isSubmitting"
+          />
+        </div>
+
+        <div class="form-group">
+          <label>Namespace:</label>
+          <input 
+            id="namespace" 
+            v-model="namespace" 
+            type="text" 
+            placeholder="default"
+            :disabled="isSubmitting"
+          />
+        </div>
+
+        <div class="form-group">
+          <label for="pod-image">Container Image:</label>
+          <input 
+            id="pod-image" 
+            v-model="podImage" 
+            type="text" 
+            placeholder="busybox"
+            :disabled="isSubmitting"
+          />
+        </div>
+
+        <div class="form-group">
+          <label>Container Command:</label>
+          <input 
+            id="command" 
+            v-model="command" 
+            type="text" 
+            placeholder='sh -c sleep 3600'
+            :disabled="isSubmitting"
+          />
+        </div>
+      </div>
+
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="create-button" 
+          @click="createPod"
+          :disabled="isSubmitting"
+        >
+          {{ isSubmitting ? 'Creating...' : 'Create' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/CreatePodGuided.css" scoped></style>

diff --git a/frontend/src/components/CreatePodMethodSelector.vue b/frontend/src/components/CreatePodMethodSelector.vue
line changes: +86/-0
index 0000000..c6bd83d
--- /dev/null
+++ b/frontend/src/components/CreatePodMethodSelector.vue
@@ -0,0 +1,86 @@
+<!-- CreatePodMethodSelector.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content" @click.stop>
+      <div class="modal-header">
+        <h3>Create Pod</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div class="method-option" @click="selectedMethod = 'guided'">
+          <div class="radio-container">
+            <div class="radio-button">
+              <div class="radio-outer">
+                <div class="radio-inner" v-if="selectedMethod === 'guided'"></div>
+              </div>
+            </div>
+            <div class="method-text">
+              <div class="method-label">Guided Creation</div>
+            </div>
+          </div>
+        </div>
+
+        <div class="method-option" @click="selectedMethod = 'yaml'">
+          <div class="radio-container">
+            <div class="radio-button">
+              <div class="radio-outer">
+                <div class="radio-inner" v-if="selectedMethod === 'yaml'"></div>
+              </div>
+            </div>
+            <div class="method-text">
+              <div class="method-label">YAML Creation</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button class="cancel-button" @click="closeModal">
+          Cancel
+        </button>
+        <button 
+          class="continue-button" 
+          @click="continueToSelected"
+          :disabled="!selectedMethod"
+        >
+          Continue
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+
+export default defineComponent({
+  name: 'CreatePodMethodSelector',
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    namespace: {
+      type: String,
+      default: 'default'
+    }
+  },
+  data() {
+    return {
+      selectedMethod: 'guided'
+    }
+  },
+  methods: {
+    closeModal() {
+      this.$emit('close')
+    },
+    continueToSelected() {
+      this.$emit('method-selected', this.selectedMethod)
+    }
+  }
+});
+</script>
+
+<style src="@/assets/css/CreatePodMethodSelector.css" scoped></style>

diff --git a/frontend/src/components/CreatePodYaml.vue b/frontend/src/components/CreatePodYaml.vue
line changes: +96/-0
index 0000000..5878d7a
--- /dev/null
+++ b/frontend/src/components/CreatePodYaml.vue
@@ -0,0 +1,96 @@
+<!-- CreatePodYaml.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content yaml-editor" @click.stop>
+      <div class="modal-header">
+        <h3>Create Pod from YAML</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div class="yaml-container">
+          <textarea 
+            v-model="yamlContent" 
+            placeholder="Paste your Pod YAML definition here..."
+            :disabled="isSubmitting"
+            class="yaml-textarea"
+          ></textarea>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="create-button" 
+          @click="createPodFromYaml"
+          :disabled="isSubmitting || !yamlContent.trim()"
+        >
+          {{ isSubmitting ? 'Creating...' : 'Create Pod' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+
+export default defineComponent({
+  name: 'CreatePodYaml',
+
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    namespace: {
+      type: String,
+      default: 'default'
+    }
+  },
+
+  emits: ['create-pod'],
+
+  data() {
+    return {
+      yamlContent: '',
+      isSubmitting: false,
+      error: ''
+    }
+  },
+  methods: {
+    closeModal() {
+      if (!this.isSubmitting) {
+        this.$emit('close')
+      }
+    },
+    async createPodFromYaml() {
+      this.isSubmitting = true;
+      this.error = '';
+
+      try {
+        let opts = {
+          yamlContent: this.yamlContent,
+          isYaml: true
+        }
+        this.$emit('create-pod', opts);
+        this.closeModal();
+      } catch (err: any) {
+        this.error = `Failed to create Pod: ${err.message || err}`;
+      } finally {
+        this.isSubmitting = false;
+      }
+    }
+  }
+});
+</script>
+
+<style src="@/assets/css/CreatePodYaml.css" scoped></style>

diff --git a/frontend/src/components/DeploymentDetails.vue b/frontend/src/components/DeploymentDetails.vue
line changes: +100/-0
index 0000000..a80e2d6
--- /dev/null
+++ b/frontend/src/components/DeploymentDetails.vue
@@ -0,0 +1,100 @@
+<template>
+  <div class="deployment-details" v-if="selectedResource">
+    <div class="details-header">
+      <span class="details-icon">🚀</span>
+      <span class="details-name">{{ selectedResource.metadata.name }}</span>
+    </div>
+    <div class="details-content">
+      <div class="details-row">
+        <span class="label">Name:</span>
+        <span class="value">{{ selectedResource.metadata.name }}</span>
+      </div>
+      <div class="details-row">
+        <span class="label">Namespace:</span>
+        <span class="value">{{ selectedResource.metadata.namespace }}</span>
+      </div>
+      <div class="details-row">
+        <span class="label">Replicas:</span>
+        <span class="value">{{ selectedResource.spec.replicas }}</span>
+      </div>
+      <div class="details-row">
+        <span class="label">Strategy:</span>
+        <span class="value">{{ selectedResource.spec.strategy?.type }}</span>
+      </div>
+      <div class="details-row">
+        <span class="label">Created:</span>
+        <span class="value">{{ formatDateTime(selectedResource.metadata.creationTimestamp) }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, PropType } from 'vue';
+import { KubernetesDeployment } from '../types/kubernetes';
+
+export default defineComponent({
+  name: 'DeploymentDetails',
+
+  props: {
+    selectedResource: {
+      type: Object as PropType<KubernetesDeployment | null>,
+      required: false,
+      default: null
+    }
+  },
+
+  methods: {
+    formatDateTime(timestamp: string): string {
+      return new Date(timestamp).toLocaleString();
+    }
+  }
+});
+</script>
+
+<style scoped>
+.deployment-details {
+  padding: 16px;
+  height: 100%;
+  overflow-y: auto;
+  background-color: #1e1e1e;
+}
+
+.details-header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 16px;
+}
+
+.details-icon {
+  font-size: 20px;
+}
+
+.details-name {
+  font-size: 16px;
+  font-weight: 500;
+  color: #ffffff;
+}
+
+.details-content {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+  gap: 8px;
+}
+
+.details-row {
+  display: flex;
+  gap: 8px;
+}
+
+.details-row .label {
+  color: #858585;
+  min-width: 100px;
+}
+
+.details-row .value {
+  color: #cccccc;
+}
+</style>
+

diff --git a/frontend/src/components/DeploymentsList.vue b/frontend/src/components/DeploymentsList.vue
line changes: +234/-0
index 0000000..50a2d46
--- /dev/null
+++ b/frontend/src/components/DeploymentsList.vue
@@ -0,0 +1,234 @@
+<script lang="ts">
+import { defineComponent, PropType, ref, computed } from 'vue';
+import { KubernetesCluster, KubernetesDeployment } from '../types/kubernetes';
+import SearchBar from './SearchBar.vue';
+import { formatAge } from '../lib/format';
+
+export default defineComponent({
+  name: 'DeploymentsList',
+
+  components: {
+    SearchBar
+  },
+
+  props: {
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: false,
+      default: null
+    },
+    deployments: {
+      type: Array as PropType<KubernetesDeployment[]>,
+      required: false,
+      default: () => []
+    }
+  },
+
+  emits: ['deployment-selected', 'restart-deployment'],
+
+  setup(props) {
+    // Search functionality
+    const searchQuery = ref('');
+
+    const filteredDeployments = computed(() => {
+      if (!searchQuery.value) {
+        return props.deployments;
+      }
+
+      const query = searchQuery.value.toLowerCase();
+
+      // Check if the query is in the format "name:deployment-name"
+      const nameSpecificMatch = query.match(/^name:(.+)$/);
+      if (nameSpecificMatch) {
+        const nameQuery = nameSpecificMatch[1].trim();
+        return props.deployments.filter(deployment => {
+          const name = deployment.metadata.name.toLowerCase();
+          return name.includes(nameQuery);
+        });
+      }
+
+      // Check if the query is in the format "namespace:namespace-name"
+      const namespaceSpecificMatch = query.match(/^namespace:(.+)$/);
+      if (namespaceSpecificMatch) {
+        const namespaceQuery = namespaceSpecificMatch[1].trim();
+        return props.deployments.filter(deployment => {
+          const namespace = deployment.metadata.namespace?.toLowerCase() || '';
+          return namespace.includes(namespaceQuery);
+        });
+      }
+
+      // Check if the query is in the format "label:key=value" or "label:key"
+      const labelSpecificMatch = query.match(/^label:(.+)$/);
+      if (labelSpecificMatch) {
+        const labelQuery = labelSpecificMatch[1].trim();
+        return props.deployments.filter(deployment => {
+          const labels = deployment.metadata.labels || {};
+          for (const key in labels) {
+            const value = labels[key].toLowerCase();
+            const keyLower = key.toLowerCase();
+
+            // Check for key=value format
+            if (labelQuery.includes('=')) {
+              const [queryKey, queryValue] = labelQuery.split('=');
+              if (keyLower === queryKey.trim().toLowerCase() && 
+                  value.includes(queryValue.trim().toLowerCase())) {
+                return true;
+              }
+            } 
+            // Check for just key
+            else if (keyLower.includes(labelQuery)) {
+              return true;
+            }
+          }
+          return false;
+        });
+      }
+
+      // Default behavior: search only in deployment names
+      return props.deployments.filter(deployment => {
+        const name = deployment.metadata.name.toLowerCase();
+        return name.includes(query);
+      });
+    });
+
+
+    // Context menu state
+    const showMenu = ref(false);
+    const menuPosition = ref({ x: 0, y: 0 });
+    const selectedContextDeployment = ref<KubernetesDeployment | null>(null);
+
+    // Show context menu
+    const showContextMenu = (event: MouseEvent, deployment: KubernetesDeployment) => {
+      event.preventDefault();
+
+      // Position the menu
+      menuPosition.value = {
+        x: event.clientX,
+        y: event.clientY
+      };
+
+      // Store the deployment that was right-clicked
+      selectedContextDeployment.value = deployment;
+
+      // Show the menu
+      showMenu.value = true;
+
+      // Add a click event listener to hide the menu when clicking outside
+      setTimeout(() => {
+        document.addEventListener('click', hideContextMenu);
+      }, 0);
+    };
+
+    // Hide context menu
+    const hideContextMenu = () => {
+      showMenu.value = false;
+      document.removeEventListener('click', hideContextMenu);
+    };
+
+    return {
+      searchQuery,
+      filteredDeployments,
+      showMenu,
+      menuPosition,
+      selectedContextDeployment,
+      showContextMenu,
+      hideContextMenu,
+			formatAge
+    };
+  },
+
+  data() {
+    return {
+      selectedDeployment: null as KubernetesDeployment | null
+    };
+  },
+
+  methods: {
+    selectDeployment(deployment: KubernetesDeployment): void {
+      const deploymentToEmit = { ...deployment };
+      if (!deploymentToEmit.kind) {
+        deploymentToEmit.kind = 'Deployment';
+      }
+      this.selectedDeployment = deploymentToEmit;
+      this.$emit('deployment-selected', deploymentToEmit);
+    },
+
+    isSelected(deployment: KubernetesDeployment): boolean {
+      return this.selectedDeployment?.metadata.name === deployment.metadata.name;
+    },
+
+    // Menu actions
+    restartDeployment() {
+      if (this.selectedContextDeployment && this.selectedCluster) {
+        this.$emit('restart-deployment', {
+          cluster: this.selectedCluster,
+          deployment: this.selectedContextDeployment
+        });
+      }
+      this.hideContextMenu();
+    },
+
+    getUniqueKey(deployment: KubernetesDeployment): string {
+      const namespace = deployment.metadata.namespace || 'default';
+      return `${namespace}-${deployment.metadata.name}-${deployment.metadata.uid}`;
+    },
+  }
+});
+</script>
+
+<template>
+  <div class="deployments-container">
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search deployments..."
+      />
+    </div>
+    <div class="table-scroll-container">
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Namespace</th>
+            <th>Ready</th>
+            <th>Up-to-date</th>
+            <th>Available</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="deployment in filteredDeployments" 
+            :key="getUniqueKey(deployment)"
+            :class="{ selected: isSelected(deployment) }"
+            @click="selectDeployment(deployment)"
+            @contextmenu.prevent="showContextMenu($event, deployment)"
+          >
+            <td>{{ deployment.metadata.name }}</td>
+            <td>{{ deployment.metadata.namespace || 'default' }}</td> <!-- Display namespace -->
+            <td>{{ `${deployment.status.readyReplicas || 0}/${deployment.spec.replicas}` }}</td>
+            <td>{{ deployment.status.updatedReplicas || 0 }}</td>
+            <td>{{ deployment.status.availableReplicas || 0 }}</td>
+            <td>{{ formatAge(deployment.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+
+    </div>
+
+    <!-- Context Menu -->
+    <div 
+      v-if="showMenu" 
+      class="context-menu" 
+      :style="{ top: menuPosition.y + 'px', left: menuPosition.x + 'px' }"
+    >
+      <div class="menu-item" @click="restartDeployment">
+        <span class="menu-text">Restart</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+
+<style src="@/assets/css/DeploymentsList.css" scoped></style>

diff --git a/frontend/src/components/EditPodYaml.vue b/frontend/src/components/EditPodYaml.vue
line changes: +146/-0
index 0000000..fda17a0
--- /dev/null
+++ b/frontend/src/components/EditPodYaml.vue
@@ -0,0 +1,146 @@
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content yaml-editor" @click.stop>
+      <div class="modal-header">
+        <h3>Edit Pod YAML</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+        <div v-if="isLoading" class="loading-message">Loading pod YAML...</div>
+
+        <div v-if="!isLoading" class="yaml-container">
+          <textarea 
+            v-model="yamlContent" 
+            :disabled="isSubmitting"
+            class="yaml-textarea"
+          ></textarea>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="update-button" 
+          @click="updatePod"
+          :disabled="isSubmitting || isLoading || !yamlContent.trim()"
+        >
+          {{ isSubmitting ? 'Updating...' : 'Update Pod' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { KubernetesPod } from '../types/kubernetes';
+import { kubernetesService } from '../lib/kubernetes';
+import { podJsonToYaml } from '../lib/lib';
+
+export default defineComponent({
+  name: 'EditPodYaml',
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    pod: {
+      type: Object as () => KubernetesPod | null,
+      default: null
+    },
+    cluster: {
+      type: Object,
+      default: null
+    }
+  },
+  data() {
+    return {
+      yamlContent: '',
+      isLoading: false,
+      isSubmitting: false,
+      error: '',
+      originalPodName: '',
+      originalNamespace: ''
+    }
+  },
+  watch: {
+    show(newVal) {
+      if (newVal && this.pod) {
+        this.fetchPodYaml();
+      } else {
+        this.yamlContent = '';
+        this.error = '';
+      }
+    }
+  },
+  methods: {
+    closeModal() {
+      if (!this.isSubmitting) {
+        this.$emit('close');
+      }
+    },
+    async fetchPodYaml() {
+      if (!this.pod || !this.cluster) return;
+
+      this.isLoading = true;
+      this.error = '';
+
+      this.originalPodName = this.pod.metadata.name;
+      this.originalNamespace = this.pod.metadata.namespace;
+
+      try {
+        const response = await kubernetesService.getPod(
+          this.cluster.contextName, 
+          this.originalNamespace,
+          this.originalPodName
+        );
+        if (response.success) {
+          this.yamlContent = podJsonToYaml(response.data);
+        } else {
+          this.error = response.msg || 'Failed to fetch pod YAML';
+        }
+      } catch (err: any) {
+        this.error = `Error fetching pod YAML: ${err.message || err}`;
+      } finally {
+        this.isLoading = false;
+      }
+    },
+    async updatePod() {
+      if (!this.pod || !this.cluster) return;
+
+      this.isSubmitting = true;
+      this.error = '';
+
+      try {
+        const response = await kubernetesService.updatePod(
+          this.cluster.contextName,
+          this.yamlContent,
+          this.originalPodName,
+          this.originalNamespace
+        );
+
+        if (response.success) {
+          this.$emit('pod-updated', response);
+          this.closeModal();
+        } else {
+          this.error = response.msg || 'Failed to update pod';
+        }
+      } catch (err: any) {
+        this.error = `Failed to update pod: ${err.message || err}`;
+      } finally {
+        this.isSubmitting = false;
+      }
+    }
+  }
+});
+</script>
+
+<style src="@/assets/css/EditPodYaml.css" scoped></style>

diff --git a/frontend/src/components/IngressesList.vue b/frontend/src/components/IngressesList.vue
line changes: +191/-0
index 0000000..b16afff
--- /dev/null
+++ b/frontend/src/components/IngressesList.vue
@@ -0,0 +1,191 @@
+<script lang="ts">
+import { defineComponent, ref, computed, PropType, onBeforeUnmount } from 'vue';
+import { KubernetesCluster, KubernetesIngress } from '../types/kubernetes';
+import SearchBar from './SearchBar.vue';
+import { formatAge } from '../lib/format';
+
+export default defineComponent({
+  name: 'IngressesList',
+  components: {
+    SearchBar
+  },
+  props: {
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster>,
+      required: false,
+      default: null
+    },
+    ingresses: {
+      type: Array as PropType<KubernetesIngress[]>,
+      required: false,
+      default: () => []
+    }
+  },
+  emits: ['ingress-selected', 'delete-ingress'],
+  data() {
+    return {
+      selectedIngress: null as KubernetesIngress | null,
+      selectedContextIngress: null as KuberneteIngress | null,
+      showMenu: false,
+      menuPosition: { x: 0, y: 0},
+    };
+  },
+  beforeUnmount() {
+    document.removeEventListener('click', this.hideContextMenu);
+  },
+  setup(props, { emit }) {
+    const searchQuery = ref('');
+    const filteredIngresses = computed(() => {
+      if (!searchQuery.value) {
+        return props.ingresses;
+      }
+
+      const query = searchQuery.value.toLowerCase();
+
+      const nameSpecificMatch = query.match(/^name:(.+)$/);
+      if (nameSpecificMatch) {
+        const nameQuery = nameSpecificMatch[1].trim();
+        return props.ingresses.filter(ingress => {
+          const name = ingress.metadata.name.toLowerCase();
+          return name.includes(nameQuery);
+        });
+      }
+
+      const namespaceSpecificMatch = query.match(/^namespace:(.+)$/);
+      if (namespaceSpecificMatch) {
+        const namespaceQuery = namespaceSpecificMatch[1].trim();
+        return props.ingresses.filter(ingress => {
+          const namespace = ingress.metadata.namespace.toLowerCase();
+          return namespace.includes(namespaceQuery);
+        });
+      }
+
+      const classSpecificMatch = query.match(/^class:(.+)$/);
+      if (classSpecificMatch) {
+        const classQuery = classSpecificMatch[1].trim();
+        return props.ingresses.filter(ingress => {
+          if (!ingress.spec.ingressClassName) {
+            return false;
+          }
+          const classname = ingress.spec.ingressClassName.toLowerCase();
+          return classname.includes(classQuery);
+        });
+      }
+
+      return props.ingresses.filter(ingress => {
+        const name = ingress.metadata.name.toLowerCase();
+        return name.includes(query);
+      });
+    });
+
+    return {
+      searchQuery,
+      filteredIngresses,
+      formatAge
+    };
+  },
+  methods: {
+    hideContextMenu(): void {
+      this.showMenu = false;
+      document.removeEventListener('click', this.hideContextMenu);
+    },
+    showContextMenu(event: MouseEvent, ingress: KubernetesIngress): void {
+      event.preventDefault();
+
+      this.menuPosition = {
+        x: event.clientX,
+        y: event.clientY
+      };
+
+      this.selectedContextIngress = ingress;
+
+      this.selectIngress(ingress);
+
+      this.showMenu = true;
+
+      setTimeout(() => {
+        document.addEventListener('click', this.hideContextMenu);
+      }, 0);
+    },
+    isSelected(ingress: KubernetesIngress): boolean {
+      if (!this.selectedIngress) return false;
+      return this.selectedIngress.metadata.name === ingress.metadata.name &&
+        this.selectedIngress.metadata.namespace === ingress.metadata.namespace &&
+        this.selectedIngress.metadata.uid === ingress.metadata.uid;
+    },
+    selectIngress(ingress: KubernetesIngress): void {
+      const ingressToEmit = { ...ingress }
+      if (!ingressToEmit.kind) {
+        ingressToEmit.kind = 'Ingress';
+      }
+      this.selectedIngress = ingressToEmit;
+      this.$emit('ingress-selected', ingressToEmit);
+    },
+    deleteIngress(): void {
+      if (this.selectedContextIngress && this.selectedCluster) {
+        this.$emit('delete-ingress', {
+          cluster: this.selectedCluster,
+          ingress: this.selectedIngress
+        });
+      }
+      this.hideContextMenu();
+    },
+    getUniqueKey(ingress: KubernetesIngress): string {
+        const namespace = ingress.metadata.namespace || 'default';
+        let key;
+        key = namespace + '-' + ingress.metadata.name + '-' + (ingress.metadata.uid || '');
+        return key;
+    }
+  },
+});
+</script>
+
+<template>
+  <div class="ingresses-container">
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search ingresses..."
+      />
+    </div>
+    <div class="table-scroll-container">
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Namespace</th>
+            <th>Class</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="ingress in filteredIngresses" 
+            :key="getUniqueKey(ingress)"
+            :class="{ selected: isSelected(ingress) }"
+            @click="selectIngress(ingress)"
+            @contextmenu="showContextMenu($event, ingress)"
+          >
+            <td>{{ ingress.metadata.name }}</td>
+            <td>{{ ingress.metadata.namespace || 'default' }}</td>
+            <td>{{ ingress.spec.ingressClassName }}</td>
+            <td>{{ formatAge(ingress.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+      <div 
+        v-if="showMenu" 
+        class="context-menu" 
+        :style="{ top: menuPosition.y + 'px', left: menuPosition.x + 'px' }"
+        @click.stop
+      >
+        <div class="menu-item" @click="deleteIngress">
+          Delete
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/IngressesList.css" scoped></style>

diff --git a/frontend/src/components/Logs.vue b/frontend/src/components/Logs.vue
line changes: +192/-0
index 0000000..35018b7
--- /dev/null
+++ b/frontend/src/components/Logs.vue
@@ -0,0 +1,192 @@
+<template>
+  <div class="logs-container">
+    <div class="logs-header">
+      <span>Logs: {{ selectedPod?.metadata?.name }}</span>
+      <button @click="$emit('close')" class="close-btn">×</button>
+    </div>
+    <div class="logs-content" ref="logsContent">
+      <pre v-html="logContent"></pre>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Logs',
+
+  props: {
+    selectedPod: {
+      type: Object,
+      required: true
+    },
+    selectedNamespace: {
+      type: Object,
+      required: true
+    },
+    selectedCluster: {
+      type: Object,
+      required: true
+    },
+    selectedContainer: {
+      type: String,
+      required: true
+    }
+  },
+
+  data() {
+    return {
+      socket: null,
+      logContent: '',
+      isConnected: false,
+      reconnectAttempts: 0,
+      maxReconnectAttempts: 3,
+      isReconnecting: false
+    }
+  },
+
+  mounted() {
+    this.connectWebSocket();
+  },
+
+  beforeUnmount() {
+    this.cleanup();
+  },
+
+  methods: {
+    connectWebSocket() {
+      if (this.isReconnecting) {
+        return;
+      }
+
+      this.isReconnecting = true;
+
+      if (this.socket) {
+        this.socket.close();
+        this.socket = null;
+      }
+
+      const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
+      const wsUrl = `${wsProtocol}//${window.location.hostname}:8081/api/pods/logs?` + 
+        `cluster=${encodeURIComponent(this.selectedCluster.server)}&` +
+        `namespace=${encodeURIComponent(this.selectedNamespace.name)}&` +
+        `pod=${encodeURIComponent(this.selectedPod.metadata.name)}&` +
+        `container=${encodeURIComponent(this.selectedContainer)}`;
+
+      try {
+        this.socket = new WebSocket(wsUrl);
+
+        this.socket.onopen = () => {
+          this.isConnected = true;
+          this.reconnectAttempts = 0;
+          this.isReconnecting = false;
+          this.logContent += 'Connected to pod logs...\n';
+          this.scrollToBottom();
+        };
+
+        this.socket.onmessage = (event) => {
+          this.logContent += event.data;
+          this.scrollToBottom();
+        };
+
+        this.socket.onclose = () => {
+          this.isConnected = false;
+          this.isReconnecting = false;
+          this.logContent += '\nConnection closed\n';
+          this.scrollToBottom();
+        };
+
+        this.socket.onerror = (error) => {
+          console.error('WebSocket error:', error);
+          this.isReconnecting = false;
+          this.logContent += '\nError: Failed to connect to pod logs\n';
+          this.scrollToBottom();
+        };
+
+      } catch (error) {
+        console.error('Failed to connect to WebSocket:', error);
+        this.isReconnecting = false;
+        this.logContent += 'Failed to connect to pod logs\n';
+        this.scrollToBottom();
+      }
+    },
+
+    scrollToBottom() {
+      if (this.$refs.logsContent) {
+        this.$refs.logsContent.scrollTop = this.$refs.logsContent.scrollHeight;
+      }
+    },
+
+    cleanup() {
+      if (this.socket) {
+        this.socket.close();
+        this.socket = null;
+      }
+      this.isConnected = false;
+      this.isReconnecting = false;
+      this.reconnectAttempts = 0;
+    }
+  }
+}
+</script>
+
+<style scoped>
+.logs-container {
+  position: fixed;
+  bottom: 20px;
+  right: 20px;
+  width: 800px;
+  height: 500px;
+  background: #1e1e1e;
+  border: 1px solid #333;
+  border-radius: 6px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
+  z-index: 1000;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.logs-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 12px;
+  background: #333;
+  color: white;
+  font-size: 14px;
+  user-select: none;
+}
+
+.close-btn {
+  background: none;
+  border: none;
+  color: white;
+  font-size: 20px;
+  cursor: pointer;
+  padding: 0 4px;
+  line-height: 1;
+}
+
+.close-btn:hover {
+  color: #ff5252;
+}
+
+.logs-content {
+  flex: 1;
+  padding: 12px;
+  overflow-y: auto;
+  background-color: #1e1e1e;
+  color: #ffffff;
+  font-family: monospace;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+  text-align: left;
+}
+
+pre {
+  margin: 0;
+  font-size: 12px;
+  line-height: 1.4;
+}
+</style>
+

diff --git a/frontend/src/components/NamespacesCreateGuided.vue b/frontend/src/components/NamespacesCreateGuided.vue
line changes: +110/-0
index 0000000..e30aa3f
--- /dev/null
+++ b/frontend/src/components/NamespacesCreateGuided.vue
@@ -0,0 +1,110 @@
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+
+export default defineComponent({
+  name: 'ResourceNamespaceCreateGuided',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true
+    },
+  },
+
+  emits: ['close', 'create-namespace'],
+
+  setup(props, { emit }) {
+    const name = ref('');
+    const isSubmitting = ref(false);
+    const error = ref('');
+
+    const resetForm = () => {
+      name.value = '';
+      error.value = '';
+      isSubmitting.value = false;
+    };
+
+    const closeModal = () => {
+      resetForm();
+      emit('close');
+    };
+
+    const createNamespace = () => {
+      // Validate form
+      if (!name.value.trim()) {
+        error.value = 'Name is required';
+        return;
+      }
+
+      isSubmitting.value = true;
+
+      let opts = {
+        definition: {
+          apiVersion: 'v1',
+          kind: 'Namespace',
+          metadata: {
+            name: name.value.trim(),
+          }
+        },
+        isYaml: false
+      }
+      emit('create-namespace', opts);
+      resetForm();
+      closeModal();
+    };
+
+    return {
+      name,
+      isSubmitting,
+      error,
+      closeModal,
+      createNamespace
+    };
+  }
+});
+</script>
+
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content" @click.stop>
+      <div class="modal-header">
+        <h3>Create Namespace</h3>
+        <button class="close-button" @click="closeModal">&times;</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div class="form-group">
+          <label for="name">Name:</label>
+          <input 
+            id="name" 
+            v-model="name" 
+            type="text" 
+            placeholder="test-namespace-1"
+            :disabled="isSubmitting"
+          />
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="create-button" 
+          @click="createNamespace"
+          :disabled="isSubmitting"
+        >
+          {{ isSubmitting ? 'Creating...' : 'Create' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/CreateResource.css" scoped></style>

diff --git a/frontend/src/components/NamespacesCreateMethodSelector.vue b/frontend/src/components/NamespacesCreateMethodSelector.vue
line changes: +91/-0
index 0000000..3747dd7
--- /dev/null
+++ b/frontend/src/components/NamespacesCreateMethodSelector.vue
@@ -0,0 +1,91 @@
+<!-- CreateNamespaceMethodSelector.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content" @click.stop>
+      <div class="modal-header">
+        <h3>Create Namespace</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div class="method-option" @click="selectedMethod = 'guided'">
+          <div class="radio-container">
+            <div class="radio-button">
+              <div class="radio-outer">
+                <div class="radio-inner" v-if="selectedMethod === 'guided'"></div>
+              </div>
+            </div>
+            <div class="method-text">
+              <div class="method-label">Guided Creation</div>
+            </div>
+          </div>
+        </div>
+
+        <div class="method-option" @click="selectedMethod = 'yaml'">
+          <div class="radio-container">
+            <div class="radio-button">
+              <div class="radio-outer">
+                <div class="radio-inner" v-if="selectedMethod === 'yaml'"></div>
+              </div>
+            </div>
+            <div class="method-text">
+              <div class="method-label">YAML Creation</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button class="cancel-button" @click="closeModal">
+          Cancel
+        </button>
+        <button 
+          class="continue-button" 
+          @click="continueToSelected"
+          :disabled="!selectedMethod"
+        >
+          Continue
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+
+export default defineComponent({
+  name: 'CreateNamespaceMethodSelector',
+
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+
+  emits: ['close', 'method-selected'],
+
+  setup(props, { emit }) {
+    // State
+    const selectedMethod = ref('guided');
+
+    // Methods
+    const closeModal = () => {
+      emit('close');
+    };
+
+    const continueToSelected = () => {
+      emit('method-selected', selectedMethod.value);
+    };
+
+    return {
+      selectedMethod,
+      closeModal,
+      continueToSelected
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/CreatePodMethodSelector.css" scoped></style>

diff --git a/frontend/src/components/NamespacesCreateYaml.vue b/frontend/src/components/NamespacesCreateYaml.vue
line changes: +101/-0
index 0000000..6012289
--- /dev/null
+++ b/frontend/src/components/NamespacesCreateYaml.vue
@@ -0,0 +1,101 @@
+<!-- NamespacesCreateYaml.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content yaml-editor" @click.stop>
+      <div class="modal-header">
+        <h3>Create Namespace from YAML</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div class="yaml-container">
+          <textarea 
+            v-model="yamlContent" 
+            placeholder="Paste your Namespace YAML definition here..."
+            :disabled="isSubmitting"
+            class="yaml-textarea"
+          ></textarea>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="create-button" 
+          @click="createNamespaceFromYaml"
+          :disabled="isSubmitting || !yamlContent.trim()"
+        >
+          {{ isSubmitting ? 'Creating...' : 'Create Namespace' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+
+export default defineComponent({
+  name: 'CreateNamespaceYaml',
+
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+
+  emits: ['close', 'create-namespace'],
+
+  setup(props, { emit }) {
+    // State
+    const yamlContent = ref('');
+    const isSubmitting = ref(false);
+    const error = ref('');
+
+    // Methods
+    const closeModal = () => {
+      if (!isSubmitting.value) {
+        emit('close');
+      }
+    };
+
+    const createNamespaceFromYaml = async () => {
+      isSubmitting.value = true;
+      error.value = '';
+
+      try {
+        const opts = {
+          yamlContent: yamlContent.value,
+          isYaml: true
+        };
+
+        emit('create-namespace', opts);
+        closeModal();
+      } catch (err: any) {
+        error.value = `Failed to create Namespace: ${err.message || err}`;
+      } finally {
+        isSubmitting.value = false;
+      }
+    };
+
+    return {
+      yamlContent,
+      isSubmitting,
+      error,
+      closeModal,
+      createNamespaceFromYaml
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/MenuDialog.css" scoped></style>

diff --git a/frontend/src/components/NamespacesEdit.vue b/frontend/src/components/NamespacesEdit.vue
line changes: +160/-0
index 0000000..4411040
--- /dev/null
+++ b/frontend/src/components/NamespacesEdit.vue
@@ -0,0 +1,160 @@
+<!-- NamespacesEdit.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content yaml-editor" @click.stop>
+      <div class="modal-header">
+        <h3>Edit Namespace YAML</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+        <div v-if="isLoading" class="loading-message">Loading namespace YAML...</div>
+
+        <div v-if="!isLoading" class="yaml-container">
+          <textarea 
+            v-model="yamlContent" 
+            :disabled="isSubmitting"
+            class="yaml-textarea"
+          ></textarea>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="update-button" 
+          @click="updateNamespace"
+          :disabled="isSubmitting || isLoading || !yamlContent.trim()"
+        >
+          {{ isSubmitting ? 'Updating...' : 'Update Namespace' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch, PropType } from 'vue';
+import { KubernetesNamespace, KubernetesCluster } from '../types/kubernetes';
+import { kubernetesService } from '../lib/kubernetes';
+import { namespaceJsonToYaml } from '../lib/lib';
+
+export default defineComponent({
+  name: 'ResourceNamespaceEdit',
+
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    namespace: {
+      type: Object as PropType<KubernetesNamespace | null>,
+      default: null
+    },
+    cluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      default: null
+    }
+  },
+
+  emits: ['close', 'namespace-updated'],
+
+  setup(props, { emit }) {
+    // State
+    const yamlContent = ref('');
+    const isLoading = ref(false);
+    const isSubmitting = ref(false);
+    const error = ref('');
+    const originalNamespaceName = ref('');
+
+    // Watch for changes to show prop
+    watch(() => props.show, (newVal) => {
+      if (newVal && props.namespace) {
+        fetchNamespaceYaml();
+      } else {
+        yamlContent.value = '';
+        error.value = '';
+      }
+    });
+
+    // Methods
+    const closeModal = () => {
+      if (!isSubmitting.value) {
+        emit('close');
+      }
+    };
+
+    const fetchNamespaceYaml = async () => {
+      if (!props.namespace || !props.cluster) return;
+
+      isLoading.value = true;
+      error.value = '';
+
+      originalNamespaceName.value = props.namespace.metadata.name;
+
+      try {
+        const response = await kubernetesService.getNamespace(
+          props.cluster.contextName,
+          originalNamespaceName.value
+        );
+
+        if (response.success) {
+          yamlContent.value = namespaceJsonToYaml(response.data);
+        } else {
+          error.value = response.msg || 'Failed to fetch namespace YAML';
+        }
+      } catch (err: any) {
+        error.value = `Error fetching namespace YAML: ${err.message || err}`;
+      } finally {
+        isLoading.value = false;
+      }
+    };
+
+    const updateNamespace = async () => {
+      if (!props.namespace || !props.cluster) return;
+
+      isSubmitting.value = true;
+      error.value = '';
+
+      try {
+        const response = await kubernetesService.updateNamespace(
+          props.cluster.contextName,
+          yamlContent.value,
+          originalNamespaceName.value
+        );
+
+        if (response.success) {
+          emit('namespace-updated', response);
+          closeModal();
+        } else {
+          error.value = response.msg || 'Failed to update namespace';
+        }
+      } catch (err: any) {
+        error.value = `Failed to update namespace: ${err.message || err}`;
+      } finally {
+        isSubmitting.value = false;
+      }
+    };
+
+    return {
+      yamlContent,
+      isLoading,
+      isSubmitting,
+      error,
+      originalNamespaceName,
+      closeModal,
+      fetchNamespaceYaml,
+      updateNamespace
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/MenuDialog.css" scoped></style>

diff --git a/frontend/src/components/NamespacesList.vue b/frontend/src/components/NamespacesList.vue
line changes: +357/-0
index 0000000..7f2c491
--- /dev/null
+++ b/frontend/src/components/NamespacesList.vue
@@ -0,0 +1,357 @@
+<!-- NamespacesList.vue -->
+<script lang="ts">
+import { defineComponent, ref, computed, PropType, onBeforeUnmount } from 'vue';
+import { KubernetesCluster, KubernetesNamespace } from '../types/kubernetes';
+import { formatAge } from '../lib/format'; 
+import SearchBar from './SearchBar.vue';
+import NamespacesCreateMethodSelector from './NamespacesCreateMethodSelector.vue';
+import NamespacesCreateGuided from './NamespacesCreateGuided.vue';
+import NamespacesCreateYaml from './NamespacesCreateYaml.vue';
+import NamespacesEdit from './NamespacesEdit.vue';
+
+export default defineComponent({
+  name: 'NamespacesList',
+
+  components: {
+    SearchBar,
+    NamespacesCreateMethodSelector,
+    NamespacesCreateGuided,
+    NamespacesCreateYaml,
+    NamespacesEdit
+  },
+
+  props: {
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster>,
+      required: false,
+      default: null
+    },
+    namespaces: {
+      type: Array as PropType<KubernetesNamespace[]>,
+      required: false,
+      default: () => []
+    }
+  },
+
+  emits: ['namespace-selected', 'delete-namespace', 'create-namespace'],
+
+  setup(props, { emit }) {
+    // State
+    const searchQuery = ref('');
+    const selectedNamespace = ref<KubernetesNamespace | null>(null);
+    const selectedContextNamespace = ref<KubernetesNamespace | null>(null);
+    const showMenu = ref(false);
+    const menuPosition = ref({ x: 0, y: 0 });
+
+    const showMethodSelector = ref(false);
+    const showCreateGuided = ref(false);
+    const showCreateYaml = ref(false);
+    const showEditYaml = ref(false);
+
+    // Computed properties
+    const filteredNamespaces = computed(() => {
+      if (!searchQuery.value) {
+        return props.namespaces;
+      }
+
+      const query = searchQuery.value.toLowerCase();
+
+      const nameSpecificMatch = query.match(/^name:(.+)$/);
+      if (nameSpecificMatch) {
+        const nameQuery = nameSpecificMatch[1].trim();
+        return props.namespaces.filter(namespace => {
+          const name = namespace.metadata.name.toLowerCase();
+          return name.includes(nameQuery);
+        });
+      }
+
+      const statusSpecificMatch = query.match(/^status:(.+)$/);
+      if (statusSpecificMatch) {
+        const statusQuery = statusSpecificMatch[1].trim();
+        return props.namespaces.filter(namespace => {
+          const status = namespace.status?.phase?.toLowerCase() || '';
+          return status.includes(statusQuery);
+        });
+      }
+
+      const labelSpecificMatch = query.match(/^label:(.+)$/);
+      if (labelSpecificMatch) {
+        const labelQuery = labelSpecificMatch[1].trim();
+        return props.namespaces.filter(namespace => {
+          const labels = namespace.metadata.labels || {};
+          for (const key in labels) {
+            const value = labels[key].toLowerCase();
+            const keyLower = key.toLowerCase();
+
+            // Check for key=value format
+            if (labelQuery.includes('=')) {
+              const [queryKey, queryValue] = labelQuery.split('=');
+              if (keyLower === queryKey.trim().toLowerCase() && 
+                  value.includes(queryValue.trim().toLowerCase())) {
+                return true;
+              }
+            } 
+            // Check for just key
+            else if (keyLower.includes(labelQuery)) {
+              return true;
+            }
+          }
+          return false;
+        });
+      }
+
+      // Default behavior: search only in namespace names
+      return props.namespaces.filter(namespace => {
+        const name = namespace.metadata.name.toLowerCase();
+        return name.includes(query);
+      });
+    });
+
+    // Methods
+    const hideContextMenu = () => {
+      showMenu.value = false;
+      document.removeEventListener('click', hideContextMenu);
+    };
+
+    const showContextMenu = (event: MouseEvent, namespace: KubernetesNamespace) => {
+      event.preventDefault();
+
+      menuPosition.value = {
+        x: event.clientX,
+        y: event.clientY
+      };
+
+      selectedContextNamespace.value = namespace;
+      selectNamespace(namespace);
+      showMenu.value = true;
+
+      setTimeout(() => {
+        document.addEventListener('click', hideContextMenu);
+      }, 0);
+    };
+
+    const isSelected = (namespace: KubernetesNamespace): boolean => {
+      if (!selectedNamespace.value) return false;
+      return selectedNamespace.value.metadata.name === namespace.metadata.name &&
+        selectedNamespace.value.metadata.uid === namespace.metadata.uid;
+    };
+
+    const selectNamespace = (namespace: KubernetesNamespace): void => {
+      const namespaceToEmit = { ...namespace };
+      if (!namespaceToEmit.kind) {
+        namespaceToEmit.kind = 'Namespace';
+      }
+      selectedNamespace.value = namespaceToEmit;
+      emit('namespace-selected', namespaceToEmit);
+    };
+
+    const deleteNamespace = (): void => {
+      if (selectedContextNamespace.value && props.selectedCluster) {
+        emit('delete-namespace', {
+          cluster: props.selectedCluster,
+          namespace: selectedNamespace.value
+        });
+      }
+      hideContextMenu();
+    };
+
+    const getUniqueKey = (namespace: KubernetesNamespace): string => {
+      return `${namespace.metadata.name}-${namespace.metadata.uid || ''}`;
+    };
+
+    const getNamespaceStatus = (namespace: KubernetesNamespace): string => {
+      return namespace.status?.phase || 'Unknown';
+    };
+
+    const getNamespaceStatusClass = (namespace: KubernetesNamespace): string => {
+      const status = getNamespaceStatus(namespace);
+      switch (status) {
+        case 'Active':
+          return 'status-active';
+        case 'Terminating':
+          return 'status-terminating';
+        default:
+          return 'status-unknown';
+      }
+    };
+
+    const editNamespace = (): void => {
+      showEditYaml.value = true;
+      hideContextMenu();
+    };
+
+    const createNamespace = (): void => {
+      showMethodSelector.value = true;
+      hideContextMenu();
+    };
+
+    const handleMethodSelected = (method: string): void => {
+      showMethodSelector.value = false;
+      if (method === 'guided') {
+        showCreateGuided.value = true;
+      } else if (method === 'yaml') {
+        showCreateYaml.value = true;
+      }
+    };
+
+    const handleUpdateNamespace = (): void => {
+      if (!props.selectedCluster) {
+        return
+      }
+      emit('load-resources')
+    }
+
+    const handleCreateNamespace = ({definition, yamlContent, isYaml}: { definition?: any, yamlContent?: string, isYaml?: boolean}): void => {
+      if (!props.selectedCluster) {
+        return
+      }
+
+      try {
+        let opts = {
+          cluster: props.selectedCluster,
+          isYaml: isYaml,
+        }
+        if (yamlContent) {
+          opts.yamlContent = yamlContent
+          emit('create-namespace', opts)
+        } else if (definition) {
+          opts.definition = definition
+          emit('create-namespace', opts)
+        } else {
+          throw new Error("Unable to emit create-namespace")
+        }
+      } catch (err: any) {
+        console.error('Failed to create namespace:', err)
+      } finally {
+        showCreateGuided.value = false
+        showCreateYaml.value = false
+      }
+    }
+
+    // Lifecycle hooks
+    onBeforeUnmount(() => {
+      document.removeEventListener('click', hideContextMenu);
+    });
+
+    // Return all refs, computed properties, and methods
+    return {
+      // State
+      showMethodSelector,
+      searchQuery,
+      selectedNamespace,
+      selectedContextNamespace,
+      showMenu,
+      showEditYaml,
+      menuPosition,
+      showCreateGuided,
+      showCreateYaml,
+
+      // Computed
+      filteredNamespaces,
+
+      // Methods
+      hideContextMenu,
+      showContextMenu,
+      isSelected,
+      selectNamespace,
+      deleteNamespace,
+      getUniqueKey,
+      getNamespaceStatus,
+      getNamespaceStatusClass,
+      formatAge,
+      handleMethodSelected,
+      createNamespace,
+      editNamespace,
+      handleCreateNamespace,
+      handleUpdateNamespace
+    };
+  }
+});
+</script>
+
+<template>
+  <div class="namespaces-container">
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search namespaces..."
+      />
+    </div>
+
+    <div v-if="filteredNamespaces.length === 0" class="no-namespaces">
+      <p v-if="searchQuery">No namespaces found matching "{{ searchQuery }}"</p>
+      <p v-else>No namespaces found.</p>
+    </div>
+
+    <div v-else class="table-scroll-container"> 
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Status</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="namespace in filteredNamespaces" 
+            :key="getUniqueKey(namespace)"
+            :class="{ selected: isSelected(namespace) }"
+            @click="selectNamespace(namespace)"
+            @contextmenu="showContextMenu($event, namespace)"
+          >
+            <td>{{ namespace.metadata.name }}</td>
+            <td :class="getNamespaceStatusClass(namespace)">{{ getNamespaceStatus(namespace) }}</td>
+            <td>{{ formatAge(namespace.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+      <div 
+        v-if="showMenu" 
+        class="context-menu" 
+        :style="{ top: menuPosition.y + 'px', left: menuPosition.x + 'px' }"
+        @click.stop
+      >
+        <div class="menu-item" @click="createNamespace">
+          Create
+        </div>
+        <div class="menu-item" @click="editNamespace">
+          Edit
+        </div>
+        <div class="menu-item" @click="deleteNamespace">
+          Delete
+        </div>
+      </div>
+    </div>
+
+    <NamespacesCreateMethodSelector
+        :show="showMethodSelector"
+        @close="showMethodSelector = false"
+        @method-selected="handleMethodSelected"
+        />
+
+    <NamespacesCreateGuided
+        :show="showCreateGuided"
+        @close="showCreateGuided = false"
+        @create-namespace="handleCreateNamespace"
+        />
+
+    <NamespacesCreateYaml
+        :show="showCreateYaml"
+        @close="showCreateYaml = false"
+        @create-namespace="handleCreateNamespace"
+        />
+
+    <NamespacesEdit
+        :show="showEditYaml"
+        :namespace="selectedContextNamespace"
+        :cluster="selectedCluster"
+        @close="showEditYaml = false"
+        @namespace-updated="handleUpdateNamespace"
+        />
+
+  </div>
+</template>
+
+<style src="@/assets/css/NamespacesList.css" scoped></style>

diff --git a/frontend/src/components/NavigationPane.vue b/frontend/src/components/NavigationPane.vue
line changes: +157/-0
index 0000000..bbbeaee
--- /dev/null
+++ b/frontend/src/components/NavigationPane.vue
@@ -0,0 +1,157 @@
+<template>
+  <div class="nav-pane">
+    <!-- Clusters Section -->
+    <div class="nav-section">
+      <div class="section-header">
+        <span class="section-title">CLUSTERS</span>
+      </div>
+      <ClusterList 
+        :clusters="clusters"
+        :selectedCluster="selectedCluster"
+        @select-cluster="handleClusterSelect"
+      />
+    </div>
+
+    <div class="nav-section" v-if="selectedCluster">
+      <div class="section-header">
+        <span class="section-title">WORKLOADS</span>
+      </div>
+      <div class="section-content">
+        <div 
+          :class="['nav-item', { active: selectedResource === 'deployments' }]"
+          @click="selectResource('deployments')"
+        >
+          <i class="fas fa-cubes"></i>
+          Deployments
+        </div>
+        <div 
+          :class="['nav-item', { active: selectedResource === 'statefulsets' }]"
+          @click="selectResource('statefulsets')"
+        >
+          <i class="fas fa-cubes"></i>
+          StatefulSets
+        </div>
+        <div 
+          :class="['nav-item', { active: selectedResource === 'pods' }]"
+          @click="selectResource('pods')"
+        >
+          <i class="fas fa-cube"></i>
+          Pods
+        </div>
+      </div>
+    </div>
+
+    <div class="nav-section" v-if="selectedCluster">
+      <div class="section-header">
+        <span class="section-title">CONFIGURATION</span>
+      </div>
+      <div class="section-content">
+        <div 
+          :class="['nav-item', { active: selectedResource === 'namespaces' }]"
+          @click="selectResource('namespaces')"
+        >
+          <i class="fas fa-cubes"></i>
+          Namespaces
+        </div>
+        <div 
+          :class="['nav-item', { active: selectedResource === 'configmaps' }]"
+          @click="selectResource('configmaps')"
+        >
+          <i class="fas fa-cubes"></i>
+          ConfigMaps
+        </div>
+        <div 
+          :class="['nav-item', { active: selectedResource === 'secrets' }]"
+          @click="selectResource('secrets')"
+        >
+          <i class="fas fa-cubes"></i>
+          Secrets
+        </div>
+      </div>
+    </div>
+
+    <div class="nav-section" v-if="selectedCluster">
+      <div class="section-header">
+        <span class="section-title">NETWORKING</span>
+      </div>
+      <div class="section-content">
+        <div 
+          :class="['nav-item', { active: selectedResource === 'ingresses' }]"
+          @click="selectResource('ingresses')"
+        >
+          <i class="fas fa-cubes"></i>
+          Ingress
+        </div>
+      </div>
+    </div>
+
+    <div class="nav-section" v-if="selectedCluster">
+      <div class="section-header">
+        <span class="section-title">STORAGE</span>
+      </div>
+      <div class="section-content">
+        <div 
+          :class="['nav-item', { active: selectedResource === 'persistentvolumeclaims' }]"
+          @click="selectResource('persistentvolumeclaims')"
+        >
+          <i class="fas fa-cubes"></i>
+          Volume Claims
+        </div>
+      </div>
+      <div class="section-content">
+        <div 
+          :class="['nav-item', { active: selectedResource === 'persistentVolumes' }]"
+          @click="selectResource('persistentVolumes')"
+        >
+          <i class="fas fa-cubes"></i>
+          Volumes
+        </div>
+      </div>
+    </div>
+
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, PropType } from 'vue';
+import { KubernetesCluster } from '../types/kubernetes';
+import ClusterList from './ClusterList.vue';
+
+export default defineComponent({
+  name: 'NavigationPane',
+
+  components: {
+    ClusterList
+  },
+
+  props: {
+    clusters: {
+      type: Array as PropType<KubernetesCluster[]>,
+      required: true,
+      default: () => []
+    },
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: false,
+      default: null
+    },
+    selectedResource: {
+      type: String as PropType<String | null>,
+      required: false,
+      default: null
+    }
+  },
+  emits: ['select-cluster', 'select-resource'],
+  methods: {
+    handleClusterSelect(cluster: KubernetesCluster) {
+      this.$emit('select-cluster', cluster);
+    },
+
+    selectResource(resource: string) {
+      this.$emit('select-resource', resource);
+    }
+  }
+});
+</script>
+
+<style src="@/assets/css/NavigationPane.css" scoped></style>

diff --git a/frontend/src/components/PersistentVolumeClaimsCreateGuided.vue b/frontend/src/components/PersistentVolumeClaimsCreateGuided.vue
line changes: +491/-0
index 0000000..098ad26
--- /dev/null
+++ b/frontend/src/components/PersistentVolumeClaimsCreateGuided.vue
@@ -0,0 +1,491 @@
+<!-- PersistentVolumeClaimsCreateGuided.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content" @click.stop>
+      <div class="modal-header">
+        <h3>Create Persistent Volume Claim</h3>
+        <button class="close-button" @click="closeModal">&times;</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div class="form-group">
+          <label for="name">Name:</label>
+          <input 
+            id="name" 
+            v-model="name" 
+            type="text" 
+            placeholder="my-pvc"
+            :disabled="isSubmitting"
+          />
+        </div>
+
+        <div class="form-group">
+          <label for="namespace">Namespace:</label>
+          <input 
+            id="namespace" 
+            v-model="namespace" 
+            type="text" 
+            placeholder="default"
+            :disabled="isSubmitting"
+          />
+        </div>
+
+        <div class="form-group">
+          <label for="storageClass">Storage Class:</label>
+          <input 
+            id="storageClass" 
+            v-model="storageClass" 
+            type="text" 
+            placeholder="standard"
+            :disabled="isSubmitting"
+          />
+          <small class="field-hint">Leave empty to use default storage class</small>
+        </div>
+
+        <div class="form-group">
+          <label for="storageSize">Storage Size:</label>
+          <div class="storage-input-group">
+            <input 
+              id="storageSize" 
+              v-model="storageSize" 
+              type="number" 
+              min="1"
+              placeholder="1"
+              :disabled="isSubmitting"
+            />
+            <select v-model="storageUnit" :disabled="isSubmitting">
+              <option value="Gi">Gi</option>
+              <option value="Mi">Mi</option>
+              <option value="Ti">Ti</option>
+            </select>
+          </div>
+        </div>
+
+        <div class="form-section">
+          <h4>Access Modes</h4>
+          <div class="checkbox-group">
+            <label class="checkbox-label">
+              <input 
+                type="checkbox" 
+                v-model="accessModes" 
+                value="ReadWriteOnce"
+                :disabled="isSubmitting"
+              />
+              ReadWriteOnce (RWO)
+              <small>Volume can be mounted as read-write by a single node</small>
+            </label>
+            <label class="checkbox-label">
+              <input 
+                type="checkbox" 
+                v-model="accessModes" 
+                value="ReadOnlyMany"
+                :disabled="isSubmitting"
+              />
+              ReadOnlyMany (ROX)
+              <small>Volume can be mounted read-only by many nodes</small>
+            </label>
+            <label class="checkbox-label">
+              <input 
+                type="checkbox" 
+                v-model="accessModes" 
+                value="ReadWriteMany"
+                :disabled="isSubmitting"
+              />
+              ReadWriteMany (RWX)
+              <small>Volume can be mounted as read-write by many nodes</small>
+            </label>
+            <label class="checkbox-label">
+              <input 
+                type="checkbox" 
+                v-model="accessModes" 
+                value="ReadWriteOncePod"
+                :disabled="isSubmitting"
+              />
+              ReadWriteOncePod (RWOP)
+              <small>Volume can be mounted as read-write by a single pod</small>
+            </label>
+          </div>
+        </div>
+
+        <div class="form-section">
+          <h4>Volume Mode</h4>
+          <div class="radio-group">
+            <label class="radio-label">
+              <input 
+                type="radio" 
+                v-model="volumeMode" 
+                value="Filesystem"
+                :disabled="isSubmitting"
+              />
+              Filesystem
+              <small>Volume is mounted into pods as a directory</small>
+            </label>
+            <label class="radio-label">
+              <input 
+                type="radio" 
+                v-model="volumeMode" 
+                value="Block"
+                :disabled="isSubmitting"
+              />
+              Block
+              <small>Volume is used as a raw block device</small>
+            </label>
+          </div>
+        </div>
+
+        <div class="form-section">
+          <h4>Labels (Optional)</h4>
+          <div 
+            v-for="(label, index) in labels" 
+            :key="index" 
+            class="label-row"
+          >
+            <div class="form-group key-field">
+              <input 
+                v-model="label.key" 
+                type="text" 
+                placeholder="key"
+                :disabled="isSubmitting"
+              />
+            </div>
+            <div class="form-group value-field">
+              <input 
+                v-model="label.value" 
+                type="text" 
+                placeholder="value"
+                :disabled="isSubmitting"
+              />
+            </div>
+            <button 
+              class="remove-button" 
+              @click="removeLabel(index)"
+              :disabled="isSubmitting || labels.length <= 1"
+            >
+              &times;
+            </button>
+          </div>
+
+          <button 
+            class="add-button" 
+            @click="addLabel"
+            :disabled="isSubmitting"
+          >
+            + Add Label
+          </button>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="create-button" 
+          @click="createPvc"
+          :disabled="!isFormValid || isSubmitting"
+        >
+          {{ isSubmitting ? 'Creating...' : 'Create PVC' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, computed, PropType } from 'vue';
+import { PersistentVolumeClaimCreateOptions } from '../types/custom';
+import { KubernetesCluster } from '../types/kubernetes';
+
+export default defineComponent({
+  name: 'PersistentVolumeClaimsCreateGuided',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true
+    },
+    cluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: false,
+      default: null
+    }
+  },
+
+  emits: ['close', 'create-pvc'],
+
+  setup(props, { emit }) {
+    const name = ref('');
+    const namespace = ref('default');
+    const storageClass = ref('');
+    const storageSize = ref(1);
+    const storageUnit = ref('Gi');
+    const accessModes = ref(['ReadWriteOnce']);
+    const volumeMode = ref('Filesystem');
+    const isSubmitting = ref(false);
+    const error = ref('');
+    const labels = ref([{ key: '', value: '' }]);
+
+    const isFormValid = computed(() => {
+      if (!name.value.trim()) return false;
+      if (!namespace.value.trim()) return false;
+      if (!storageSize.value || storageSize.value <= 0) return false;
+      if (accessModes.value.length === 0) return false;
+      return true;
+    });
+
+    const addLabel = () => {
+      labels.value.push({ key: '', value: '' });
+    };
+
+    const removeLabel = (index: number) => {
+      if (labels.value.length > 1) {
+        labels.value.splice(index, 1);
+      }
+    };
+
+    const resetForm = () => {
+      name.value = '';
+      namespace.value = 'default';
+      storageClass.value = '';
+      storageSize.value = 1;
+      storageUnit.value = 'Gi';
+      accessModes.value = ['ReadWriteOnce'];
+      volumeMode.value = 'Filesystem';
+      labels.value = [{ key: '', value: '' }];
+      error.value = '';
+      isSubmitting.value = false;
+    };
+
+    const closeModal = () => {
+      resetForm();
+      emit('close');
+    };
+
+    const createPvc = () => {
+      if (!name.value.trim()) {
+        error.value = 'Name is required';
+        return;
+      }
+
+      if (!namespace.value.trim()) {
+        error.value = 'Namespace is required';
+        return;
+      }
+
+      if (!props.cluster) {
+        error.value = 'No cluster selected';
+        return;
+      }
+
+      if (!storageSize.value || storageSize.value <= 0) {
+        error.value = 'Storage size must be greater than 0';
+        return;
+      }
+
+      if (accessModes.value.length === 0) {
+        error.value = 'At least one access mode must be selected';
+        return;
+      }
+
+      isSubmitting.value = true;
+
+      // Build labels object
+      const labelsObj = {};
+      labels.value.forEach(label => {
+        if (label.key.trim() && label.value.trim()) {
+          labelsObj[label.key.trim()] = label.value.trim();
+        }
+      });
+
+      // Build PVC spec
+      const spec: any = {
+        accessModes: accessModes.value,
+        resources: {
+          requests: {
+            storage: `${storageSize.value}${storageUnit.value}`
+          }
+        },
+        volumeMode: volumeMode.value
+      };
+
+      // Add storage class if specified
+      if (storageClass.value.trim()) {
+        spec.storageClassName = storageClass.value.trim();
+      }
+
+      // Build metadata
+      const metadata: any = {
+        name: name.value.trim(),
+        namespace: namespace.value.trim()
+      };
+
+      // Add labels if any
+      if (Object.keys(labelsObj).length > 0) {
+        metadata.labels = labelsObj;
+      }
+
+      const opts: PersistentVolumeClaimCreateOptions = {
+        context: props.cluster.contextName,
+        opts: {
+          definition: {
+            apiVersion: 'v1',
+            kind: 'PersistentVolumeClaim',
+            metadata: metadata,
+            spec: spec
+          },
+          isYaml: false
+        }
+      };
+
+      emit('create-pvc', opts);
+      resetForm();
+      closeModal();
+    };
+
+    return {
+      name,
+      namespace,
+      storageClass,
+      storageSize,
+      storageUnit,
+      accessModes,
+      volumeMode,
+      labels,
+      isSubmitting,
+      error,
+      isFormValid,
+      addLabel,
+      removeLabel,
+      closeModal,
+      createPvc
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/CreateResource.css" scoped></style>
+
+<style scoped>
+.storage-input-group {
+  display: flex;
+  gap: 0.5rem;
+}
+
+.storage-input-group input {
+  flex: 1;
+}
+
+.storage-input-group select {
+  width: 80px;
+  padding: 0.5rem;
+  border: 1px solid #444;
+  border-radius: 4px;
+  background-color: #2d2d2d;
+  color: #ffffff;
+}
+
+.checkbox-group {
+  display: flex;
+  flex-direction: column;
+  gap: 0.75rem;
+}
+
+.checkbox-label {
+  display: flex;
+  flex-direction: column;
+  gap: 0.25rem;
+  cursor: pointer;
+}
+
+.checkbox-label input[type="checkbox"] {
+  width: auto;
+  margin-right: 0.5rem;
+}
+
+.radio-group {
+  display: flex;
+  flex-direction: column;
+  gap: 0.75rem;
+}
+
+.radio-label {
+  display: flex;
+  flex-direction: column;
+  gap: 0.25rem;
+  cursor: pointer;
+}
+
+.radio-label input[type="radio"] {
+  width: auto;
+  margin-right: 0.5rem;
+}
+
+.field-hint {
+  color: #999;
+  font-size: 0.8rem;
+  margin-top: 0.25rem;
+  display: block;
+}
+
+.label-row {
+  display: flex;
+  gap: 0.5rem;
+  align-items: flex-start;
+  margin-bottom: 0.5rem;
+}
+
+.key-field,
+.value-field {
+  flex: 1;
+  margin-bottom: 0;
+}
+
+.remove-button {
+  background-color: #dc3545;
+  border: 1px solid #dc3545;
+  color: white;
+  border-radius: 4px;
+  width: 30px;
+  height: 30px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: 1.5rem;
+}
+
+.remove-button:hover:not(:disabled) {
+  background-color: #c82333;
+}
+
+.remove-button:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+
+.add-button {
+  background-color: #28a745;
+  border: 1px solid #28a745;
+  color: white;
+  padding: 0.5rem 1rem;
+  border-radius: 4px;
+  cursor: pointer;
+  margin-top: 0.5rem;
+}
+
+.add-button:hover:not(:disabled) {
+  background-color: #218838;
+}
+
+.add-button:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+</style>
+

diff --git a/frontend/src/components/PersistentVolumeClaimsCreateYaml.vue b/frontend/src/components/PersistentVolumeClaimsCreateYaml.vue
line changes: +339/-0
index 0000000..26599b3
--- /dev/null
+++ b/frontend/src/components/PersistentVolumeClaimsCreateYaml.vue
@@ -0,0 +1,339 @@
+<!-- PersistentVolumeClaimsCreateYaml.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content yaml-editor" @click.stop>
+      <div class="modal-header">
+        <h3>Create Persistent Volume Claim - YAML</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div class="yaml-container">
+          <textarea 
+            v-model="yamlContent" 
+            :disabled="isSubmitting"
+            class="yaml-textarea"
+            placeholder="Enter your PersistentVolumeClaim YAML here..."
+          ></textarea>
+        </div>
+
+        <div class="template-section">
+          <button 
+            class="template-button" 
+            @click="loadTemplate"
+            :disabled="isSubmitting"
+          >
+            Load Template
+          </button>
+          <span class="template-hint">Click to load a basic PVC template</span>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="create-button" 
+          @click="createPvc"
+          :disabled="isSubmitting || !yamlContent.trim()"
+        >
+          {{ isSubmitting ? 'Creating...' : 'Create PVC' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+import { PersistentVolumeClaimCreateOptions } from '../types/custom';
+
+export default defineComponent({
+  name: 'PersistentVolumeClaimsCreateYaml',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true
+    }
+  },
+
+  emits: ['close', 'create-pvc'],
+
+  setup(props, { emit }) {
+    // State
+    const yamlContent = ref('');
+    const isSubmitting = ref(false);
+    const error = ref('');
+
+    // Template YAML for PVC
+    const pvcTemplate = `apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  name: my-pvc
+  namespace: default
+spec:
+  accessModes:
+    - ReadWriteOnce
+  resources:
+    requests:
+      storage: 1Gi
+  storageClassName: standard`;
+
+    // Methods
+    const closeModal = () => {
+      if (!isSubmitting.value) {
+        resetForm();
+        emit('close');
+      }
+    };
+
+    const resetForm = () => {
+      yamlContent.value = '';
+      error.value = '';
+      isSubmitting.value = false;
+    };
+
+    const loadTemplate = () => {
+      yamlContent.value = pvcTemplate;
+      error.value = '';
+    };
+
+    const validateYaml = (yaml: string): boolean => {
+      try {
+        // Basic validation - check if it looks like a PVC
+        const trimmed = yaml.trim();
+        if (!trimmed) {
+          error.value = 'YAML content cannot be empty';
+          return false;
+        }
+
+        // Check for required fields
+        if (!trimmed.includes('kind:') || !trimmed.includes('PersistentVolumeClaim')) {
+          error.value = 'YAML must contain "kind: PersistentVolumeClaim"';
+          return false;
+        }
+
+        if (!trimmed.includes('apiVersion:')) {
+          error.value = 'YAML must contain "apiVersion"';
+          return false;
+        }
+
+        if (!trimmed.includes('metadata:')) {
+          error.value = 'YAML must contain "metadata" section';
+          return false;
+        }
+
+        if (!trimmed.includes('spec:')) {
+          error.value = 'YAML must contain "spec" section';
+          return false;
+        }
+
+        return true;
+      } catch (err) {
+        error.value = 'Invalid YAML format';
+        return false;
+      }
+    };
+
+    const createPvc = () => {
+      error.value = '';
+
+      if (!yamlContent.value.trim()) {
+        error.value = 'YAML content is required';
+        return;
+      }
+
+      if (!validateYaml(yamlContent.value)) {
+        return;
+      }
+
+      isSubmitting.value = true;
+
+      try {
+        const opts: PersistentVolumeClaimCreateOptions = {
+          context: '', // This will be set by the parent component
+          opts: {
+            yamlContent: yamlContent.value.trim(),
+            isYaml: true
+          }
+        };
+
+        emit('create-pvc', opts);
+        resetForm();
+        closeModal();
+      } catch (err: any) {
+        error.value = `Failed to create PVC: ${err.message || err}`;
+        isSubmitting.value = false;
+      }
+    };
+
+    return {
+      yamlContent,
+      isSubmitting,
+      error,
+      closeModal,
+      loadTemplate,
+      createPvc
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/MenuDialog.css" scoped></style>
+
+<style scoped>
+.yaml-container {
+  margin-bottom: 1rem;
+}
+
+.yaml-textarea {
+  width: 100%;
+  height: 400px;
+  padding: 1rem;
+  border: 1px solid #444;
+  border-radius: 4px;
+  background-color: #1e1e1e;
+  color: #ffffff;
+  font-family: 'Courier New', Consolas, monospace;
+  font-size: 14px;
+  line-height: 1.4;
+  resize: vertical;
+  min-height: 300px;
+  max-height: 600px;
+}
+
+.yaml-textarea:focus {
+  outline: none;
+  border-color: #007acc;
+  box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
+}
+
+.yaml-textarea:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.yaml-textarea::placeholder {
+  color: #666;
+  font-style: italic;
+}
+
+.template-section {
+  display: flex;
+  align-items: center;
+  gap: 0.75rem;
+  margin-bottom: 1rem;
+  padding: 0.75rem;
+  background-color: #333;
+  border-radius: 4px;
+  border: 1px solid #444;
+}
+
+.template-button {
+  padding: 0.5rem 1rem;
+  border: 1px solid #007acc;
+  background-color: #007acc;
+  color: white;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 0.9rem;
+  transition: all 0.2s;
+}
+
+.template-button:hover:not(:disabled) {
+  background-color: #005a9e;
+  border-color: #005a9e;
+}
+
+.template-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.template-hint {
+  color: #ccc;
+  font-size: 0.85rem;
+  font-style: italic;
+}
+
+.error-message {
+  background-color: #4a1a1a;
+  border: 1px solid #cc4444;
+  color: #ff6b6b;
+  padding: 0.75rem;
+  border-radius: 4px;
+  margin-bottom: 1rem;
+  font-size: 0.9rem;
+}
+
+.modal-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 0.5rem;
+  padding: 1rem 1.5rem;
+  border-top: 1px solid #444;
+}
+
+.cancel-button {
+  padding: 0.5rem 1rem;
+  border: 1px solid #666;
+  background-color: transparent;
+  color: #ccc;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.cancel-button:hover:not(:disabled) {
+  background-color: #444;
+  border-color: #777;
+}
+
+.create-button {
+  padding: 0.5rem 1rem;
+  border: 1px solid #28a745;
+  background-color: #28a745;
+  color: white;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.create-button:hover:not(:disabled) {
+  background-color: #218838;
+  border-color: #1e7e34;
+}
+
+.create-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+/* Responsive design */
+@media (max-width: 768px) {
+  .yaml-textarea {
+    height: 300px;
+    font-size: 12px;
+  }
+
+  .template-section {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 0.5rem;
+  }
+
+  .template-button {
+    font-size: 0.8rem;
+    padding: 0.4rem 0.8rem;
+  }
+}
+</style>
+

diff --git a/frontend/src/components/PersistentVolumeClaimsEdit.vue b/frontend/src/components/PersistentVolumeClaimsEdit.vue
line changes: +424/-0
index 0000000..c72ea3d
--- /dev/null
+++ b/frontend/src/components/PersistentVolumeClaimsEdit.vue
@@ -0,0 +1,424 @@
+<!-- PersistentVolumeClaimsEdit.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content yaml-editor" @click.stop>
+      <div class="modal-header">
+        <h3>Edit Persistent Volume Claim</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div v-if="pvc" class="resource-info">
+          <div class="info-item">
+            <strong>Name:</strong> {{ pvc.metadata.name }}
+          </div>
+          <div class="info-item">
+            <strong>Namespace:</strong> {{ pvc.metadata.namespace }}
+          </div>
+          <div class="info-item">
+            <strong>Status:</strong> {{ pvc.status?.phase || 'Unknown' }}
+          </div>
+        </div>
+
+        <div class="yaml-container">
+          <textarea 
+            v-model="yamlContent" 
+            :disabled="isSubmitting"
+            class="yaml-textarea"
+            placeholder="Loading PVC YAML..."
+          ></textarea>
+        </div>
+
+        <div class="warning-section">
+          <div class="warning-message">
+            <strong>⚠️ Warning:</strong> Editing a PVC may have limitations. Some fields like storage size can only be increased (if supported by storage class), and access modes cannot be changed after creation.
+          </div>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="update-button" 
+          @click="updatePvc"
+          :disabled="isSubmitting || !yamlContent.trim() || !hasChanges"
+        >
+          {{ isSubmitting ? 'Updating...' : 'Update PVC' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, computed, watch, PropType } from 'vue';
+import { KubernetesCluster, KubernetesPersistentVolumeClaim } from '../types/kubernetes';
+import { PersistentVolumeClaimUpdateOptions } from '../types/custom';
+
+export default defineComponent({
+  name: 'PersistentVolumeClaimsEdit',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true
+    },
+    pvc: {
+      type: Object as PropType<KubernetesPersistentVolumeClaim | null>,
+      required: false,
+      default: null
+    },
+    cluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: false,
+      default: null
+    }
+  },
+
+  emits: ['close', 'update-pvc'],
+
+  setup(props, { emit }) {
+    // State
+    const yamlContent = ref('');
+    const originalYaml = ref('');
+    const isSubmitting = ref(false);
+    const error = ref('');
+
+    // Computed
+    const hasChanges = computed(() => {
+      return yamlContent.value.trim() !== originalYaml.value.trim();
+    });
+
+    // Methods
+    const closeModal = () => {
+      if (!isSubmitting.value) {
+        resetForm();
+        emit('close');
+      }
+    };
+
+    const resetForm = () => {
+      yamlContent.value = '';
+      originalYaml.value = '';
+      error.value = '';
+      isSubmitting.value = false;
+    };
+
+    const convertToYaml = (obj: any): string => {
+      try {
+        // Simple YAML conversion - in a real app, you'd use a proper YAML library
+        const cleanObj = JSON.parse(JSON.stringify(obj));
+
+        // Remove fields that shouldn't be edited
+        if (cleanObj.metadata) {
+          delete cleanObj.metadata.resourceVersion;
+          delete cleanObj.metadata.uid;
+          delete cleanObj.metadata.selfLink;
+          delete cleanObj.metadata.creationTimestamp;
+          delete cleanObj.metadata.generation;
+          delete cleanObj.metadata.managedFields;
+        }
+
+        // Remove status as it's read-only
+        delete cleanObj.status;
+
+        return JSON.stringify(cleanObj, null, 2)
+          .replace(/"/g, '')
+          .replace(/,$/gm, '')
+          .replace(/\{/g, '')
+          .replace(/\}/g, '')
+          .replace(/\[/g, '')
+          .replace(/\]/g, '')
+          .replace(/^\s*$/gm, '')
+          .split('\n')
+          .filter(line => line.trim())
+          .map(line => {
+            const indent = '  '.repeat((line.match(/^\s*/)?.[0]?.length || 0) / 2);
+            const content = line.trim();
+            if (content.endsWith(':')) {
+              return `${indent}${content}`;
+            }
+            const [key, ...valueParts] = content.split(':');
+            const value = valueParts.join(':').trim();
+            return value ? `${indent}${key}: ${value}` : `${indent}${key}:`;
+          })
+          .join('\n');
+      } catch (err) {
+        console.error('Error converting to YAML:', err);
+        return JSON.stringify(obj, null, 2);
+      }
+    };
+
+    const loadPvcYaml = () => {
+      if (!props.pvc) {
+        yamlContent.value = '';
+        originalYaml.value = '';
+        return;
+      }
+
+      try {
+        const yaml = convertToYaml(props.pvc);
+        yamlContent.value = yaml;
+        originalYaml.value = yaml;
+        error.value = '';
+      } catch (err: any) {
+        error.value = `Failed to load PVC YAML: ${err.message || err}`;
+        yamlContent.value = '';
+        originalYaml.value = '';
+      }
+    };
+
+    const validateYaml = (yaml: string): boolean => {
+      try {
+        const trimmed = yaml.trim();
+        if (!trimmed) {
+          error.value = 'YAML content cannot be empty';
+          return false;
+        }
+
+        // Basic validation for PVC
+        if (!trimmed.includes('kind:') || !trimmed.includes('PersistentVolumeClaim')) {
+          error.value = 'YAML must contain "kind: PersistentVolumeClaim"';
+          return false;
+        }
+
+        if (!trimmed.includes('apiVersion:')) {
+          error.value = 'YAML must contain "apiVersion"';
+          return false;
+        }
+
+        if (!trimmed.includes('metadata:')) {
+          error.value = 'YAML must contain "metadata" section';
+          return false;
+        }
+
+        if (!trimmed.includes('spec:')) {
+          error.value = 'YAML must contain "spec" section';
+          return false;
+        }
+
+        return true;
+      } catch (err) {
+        error.value = 'Invalid YAML format';
+        return false;
+      }
+    };
+
+    const updatePvc = (): void => {
+      if (!props.pvc || !props.cluster) {
+        error.value = 'Missing PVC or cluster information';
+        return;
+      }
+
+      error.value = '';
+
+      if (!yamlContent.value.trim()) {
+        error.value = 'YAML content is required';
+        return;
+      }
+
+      if (!validateYaml(yamlContent.value)) {
+        return;
+      }
+
+      if (!hasChanges.value) {
+        error.value = 'No changes detected';
+        return;
+      }
+
+      isSubmitting.value = true;
+
+      try {
+        const opts: PersistentVolumeClaimUpdateOptions = {
+          context: props.cluster.contextName,
+          definition: yamlContent.value.trim(),
+          origNamespace: props.pvc.metadata.namespace,
+          origName: props.pvc.metadata.name
+        };
+
+        emit('update-pvc', opts);
+        closeModal();
+      } catch (err: any) {
+        error.value = `Failed to update PVC: ${err.message || err}`;
+        isSubmitting.value = false;
+      }
+    };
+
+    // Watchers
+    watch(() => props.show, (newShow) => {
+      if (newShow && props.pvc) {
+        loadPvcYaml();
+      }
+    });
+
+    watch(() => props.pvc, (newPvc) => {
+      if (newPvc && props.show) {
+        loadPvcYaml();
+      }
+    });
+
+    return {
+      yamlContent,
+      isSubmitting,
+      error,
+      hasChanges,
+      closeModal,
+      updatePvc
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/MenuDialog.css" scoped></style>
+
+<style scoped>
+.resource-info {
+  background-color: #333;
+  border: 1px solid #444;
+  border-radius: 4px;
+  padding: 1rem;
+  margin-bottom: 1rem;
+}
+
+.info-item {
+  margin-bottom: 0.5rem;
+  color: #ccc;
+}
+
+.info-item:last-child {
+  margin-bottom: 0;
+}
+
+.info-item strong {
+  color: #fff;
+  margin-right: 0.5rem;
+}
+
+.yaml-container {
+  margin-bottom: 1rem;
+}
+
+.yaml-textarea {
+  width: 100%;
+  height: 400px;
+  padding: 1rem;
+  border: 1px solid #444;
+  border-radius: 4px;
+  background-color: #1e1e1e;
+  color: #ffffff;
+  font-family: 'Courier New', Consolas, monospace;
+  font-size: 14px;
+  line-height: 1.4;
+  resize: vertical;
+  min-height: 300px;
+  max-height: 600px;
+}
+
+.yaml-textarea:focus {
+  outline: none;
+  border-color: #007acc;
+  box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
+}
+
+.yaml-textarea:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.yaml-textarea::placeholder {
+  color: #666;
+  font-style: italic;
+}
+
+.warning-section {
+  margin-bottom: 1rem;
+}
+
+.warning-message {
+  background-color: #4a3a1a;
+  border: 1px solid #cc9944;
+  color: #ffcc66;
+  padding: 0.75rem;
+  border-radius: 4px;
+  font-size: 0.9rem;
+  line-height: 1.4;
+}
+
+.error-message {
+  background-color: #4a1a1a;
+  border: 1px solid #cc4444;
+  color: #ff6b6b;
+  padding: 0.75rem;
+  border-radius: 4px;
+  margin-bottom: 1rem;
+  font-size: 0.9rem;
+}
+
+.modal-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 0.5rem;
+  padding: 1rem 1.5rem;
+  border-top: 1px solid #444;
+}
+
+.cancel-button {
+  padding: 0.5rem 1rem;
+  border: 1px solid #666;
+  background-color: transparent;
+  color: #ccc;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.cancel-button:hover:not(:disabled) {
+  background-color: #444;
+  border-color: #777;
+}
+
+.update-button {
+  padding: 0.5rem 1rem;
+  border: 1px solid #007acc;
+  background-color: #007acc;
+  color: white;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.update-button:hover:not(:disabled) {
+  background-color: #005a9e;
+  border-color: #005a9e;
+}
+
+.update-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+/* Responsive design */
+@media (max-width: 768px) {
+  .yaml-textarea {
+    height: 300px;
+    font-size: 12px;
+  }
+
+  .resource-info {
+    padding: 0.75rem;
+  }
+
+  .info-item {
+    font-size: 0.9rem;
+  }
+}
+</style>
+

diff --git a/frontend/src/components/PersistentVolumeClaimsList.vue b/frontend/src/components/PersistentVolumeClaimsList.vue
line changes: +468/-0
index 0000000..d4e0324
--- /dev/null
+++ b/frontend/src/components/PersistentVolumeClaimsList.vue
@@ -0,0 +1,468 @@
+<!-- PersistentVolumeClaimsList.vue -->
+<script lang="ts">
+import { defineComponent, ref, computed, PropType, onBeforeUnmount } from 'vue';
+import { KubernetesCluster, KubernetesPersistentVolumeClaim } from '../types/kubernetes';
+import { formatAge } from '../lib/format'; 
+import SearchBar from './SearchBar.vue';
+import { PersistentVolumeClaimUpdateOptions } from '../types/custom';
+
+export default defineComponent({
+  name: 'PersistentVolumeClaimsList',
+
+  components: {
+    SearchBar
+  },
+
+  props: {
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster>,
+      required: false,
+      default: null
+    },
+    persistentvolumeclaims: {
+      type: Array as PropType<KubernetesPersistentVolumeClaim[]>,
+      required: false,
+      default: () => []
+    }
+  },
+
+  emits: ['persistentVolumeClaim-selected', 'delete-persistentvolumeclaim', 'create-persistentvolumeclaim'],
+
+  setup(props, { emit }) {
+    // State
+    const showCreate = ref(false);
+    const isSubmitting = ref(false);
+    const searchQuery = ref('');
+    const selectedPersistentVolumeClaim = ref<KubernetesPersistentVolumeClaim | null>(null);
+    const selectedContextPersistentVolumeClaim = ref<KubernetesPersistentVolumeClaim | null>(null);
+    const showMenu = ref(false);
+    const menuPosition = ref({ x: 0, y: 0 });
+
+    const formName = ref('');
+    const formNamespace = ref('');
+    const formStorageClass = ref('local-path');
+    const formAccessModes = ref(['ReadWriteOnce']);
+    const formCapacity = ref('10');
+    const formCapacityUnit = ref('Gi');
+    const formError = ref('');
+
+    const resetForm = () => {
+      formName.value = '';
+      formNamespace.value = '';
+      formStorageClass.value = '';
+      formError.value = '';
+      formAccessModes.value = ['ReadWriteOnce'];
+      formCapacity.value = '';
+      formCapacityUnit.value = '';
+      isSubmitting.value = false;
+    };
+
+    const cancelForm = (): void => {
+      resetForm();
+      closeModal();
+    }
+
+    const createPersistentVolumeClaim = (): void => {
+      if (!formName.value.trim()) {
+        formError.value = 'Name is required';
+        return;
+      }
+
+      if (!formNamespace.value.trim()) {
+        formError.value = 'Namespace is required';
+        return;
+      }
+
+      if (!formStorageClass.value.trim()) {
+        formError.value = 'Storage Class is required';
+        return;
+      }
+
+      if (!formCapacity.value) {
+        formError.value = 'Capacity is required';
+        return;
+      }
+
+      if (formCapacity.value <= 0) {
+        formError.value = 'Capacity must be 1 or greater';
+        return;
+      }
+
+      if (formAccessModes.value.length === 0) {
+        formError.value = 'Access Modes is required';
+        return;
+      }
+
+      const persistentVolumeClaim: KubernetesPersistentVolumeClaim = {
+        apiVersion: 'v1',
+        kind: 'PersistentVolumeClaim',
+        metadata: {
+          name: formName.value.trim(),
+          namespace: formNamespace.value.trim(),
+        },
+        spec: {
+          storageClassName: formStorageClass.value.trim(),
+          accessModes: formAccessModes.value,
+          resources: {
+            requests: {
+              storage: `${formCapacity.value}${formCapacityUnit.value}`,
+            }
+          }
+        }
+      };
+
+      isSubmitting.value = true;
+      resetForm();
+      closeModal();
+      hideContextMenu();
+      if (props.selectedCluster) {
+        try {
+          emit('create-persistentvolumeclaim', {
+            cluster: props.selectedCluster,
+            persistentVolumeClaim: persistentVolumeClaim
+          });
+        } catch (error) {
+          alert(error);
+        }
+      } else {
+        alert("Failed to get selected cluster");
+      }
+      resetForm();
+      closeModal();
+      hideContextMenu();
+    };
+
+    const filteredPersistentVolumeClaims = computed(() => {
+      if (!searchQuery.value) {
+        return props.persistentvolumeclaims;
+      }
+
+      const query = searchQuery.value.toLowerCase();
+
+      const nameSpecificMatch = query.match(/^name:(.+)$/);
+      if (nameSpecificMatch) {
+        const nameQuery = nameSpecificMatch[1].trim();
+        return props.persistentvolumeclaims.filter(persistentvolumeclaim => {
+          const name = persistentvolumeclaim.metadata.name.toLowerCase();
+          return name.includes(nameQuery);
+        });
+      }
+
+      const namespaceSpecificMatch = query.match(/^namespace:(.+)$/);
+      if (namespaceSpecificMatch) {
+        const namespaceQuery = namespaceSpecificMatch[1].trim();
+        return props.persistentvolumeclaims.filter(persistentvolumeclaim => {
+          const namespace = persistentvolumeclaim.metadata.namespace.toLowerCase();
+          return namespace.includes(namespaceQuery);
+        });
+      }
+
+      const statusSpecificMatch = query.match(/^status:(.+)$/);
+      if (statusSpecificMatch) {
+        const statusQuery = statusSpecificMatch[1].trim();
+        return props.persistentvolumeclaims.filter(persistentvolumeclaim => {
+          const status = persistentvolumeclaim.status?.phase?.toLowerCase() || '';
+          return status.includes(statusQuery);
+        });
+      }
+
+      const labelSpecificMatch = query.match(/^label:(.+)$/);
+      if (labelSpecificMatch) {
+        const labelQuery = labelSpecificMatch[1].trim();
+        return props.persistentvolumeclaims.filter(persistentvolumeclaim => {
+          const labels = persistentvolumeclaim.metadata.labels || {};
+          for (const key in labels) {
+            const value = labels[key].toLowerCase();
+            const keyLower = key.toLowerCase();
+
+            // Check for key=value format
+            if (labelQuery.includes('=')) {
+              const [queryKey, queryValue] = labelQuery.split('=');
+              if (keyLower === queryKey.trim().toLowerCase() && 
+                  value.includes(queryValue.trim().toLowerCase())) {
+                return true;
+              }
+            } 
+            // Check for just key
+            else if (keyLower.includes(labelQuery)) {
+              return true;
+            }
+          }
+          return false;
+        });
+      }
+
+      // Default behavior: search only in persistentvolumeclaim names
+      return props.persistentvolumeclaims.filter(persistentvolumeclaim => {
+        const name = persistentvolumeclaim.metadata.name.toLowerCase();
+        return name.includes(query);
+      });
+    });
+
+    // Methods
+    const hideContextMenu = () => {
+      showMenu.value = false;
+      document.removeEventListener('click', hideContextMenu);
+    };
+
+    const showContextMenu = (event: MouseEvent, persistentvolumeclaim: KubernetesPersistentVolumeClaim) => {
+      event.preventDefault();
+
+      menuPosition.value = {
+        x: event.clientX,
+        y: event.clientY
+      };
+
+      selectedContextPersistentVolumeClaim.value = persistentvolumeclaim;
+      selectPersistentVolumeClaim(persistentvolumeclaim);
+      showMenu.value = true;
+
+      setTimeout(() => {
+        document.addEventListener('click', hideContextMenu);
+      }, 0);
+    };
+
+    const closeModal = () => {
+      showCreate.value = false;
+    };
+
+    const isSelected = (persistentvolumeclaim: KubernetesPersistentVolumeClaim): boolean => {
+      if (!selectedPersistentVolumeClaim.value) return false;
+      return selectedPersistentVolumeClaim.value.metadata.name === persistentvolumeclaim.metadata.name &&
+        selectedPersistentVolumeClaim.value.metadata.uid === persistentvolumeclaim.metadata.uid;
+    };
+
+    const selectPersistentVolumeClaim = (persistentVolumeClaim: KubernetesPersistentVolumeClaim): void => {
+      try {
+        const payload = { ...persistentVolumeClaim };
+        if (!payload.kind) {
+          payload.kind = 'PersistentVolumeClaim';
+        }
+        selectedPersistentVolumeClaim.value = payload;
+        emit('persistentVolumeClaim-selected', payload);
+      } catch (error) {
+        alert("Failed to select Persistent Volume Claim:", error);
+      }
+    };
+
+    const deletePersistentVolumeClaim = (): void => {
+      if (selectedContextPersistentVolumeClaim.value && props.selectedCluster) {
+        try {
+          emit('delete-persistentvolumeclaim', {
+            cluster: props.selectedCluster,
+            persistentVolumeClaim: selectedContextPersistentVolumeClaim.value
+          });
+        } catch (error) {
+          alert(error);
+        }
+      }
+      hideContextMenu();
+    };
+
+    const getUniqueKey = (persistentvolumeclaim: KubernetesPersistentVolumeClaim): string => {
+      return `${persistentvolumeclaim.metadata.name}-${persistentvolumeclaim.metadata.uid || ''}`;
+    };
+
+    const getPersistentVolumeClaimStorageClass = (persistentVolumeClaim: KubernetesPersistentVolumeClaim): string => {
+      return persistentVolumeClaim.spec?.storageClassName || 'default';
+    };
+
+    const getPersistentVolumeClaimAccessModes = (persistentVolumeClaim: KubernetesPersistentVolumeClaim): string => {
+      const accessModes = persistentVolumeClaim.spec?.accessModes?.join(', ') || 'Unknown';
+      switch (accessModes) {
+        case "ReadWriteOnce":
+          return "RWO";
+        default:
+          return accessModes;
+      }
+    };
+
+    const getPersistentVolumeClaimStatus = (persistentvolumeclaim: KubernetesPersistentVolumeClaim): string => {
+      return persistentvolumeclaim.status?.phase || 'Unknown';
+    };
+
+    const getPersistentVolumeClaimStatusClass = (persistentvolumeclaim: KubernetesPersistentVolumeClaim): string => {
+      const status = getPersistentVolumeClaimStatus(persistentvolumeclaim);
+      switch (status) {
+        case 'Bound':
+          return 'status-active';
+        case 'Pending':
+          return 'status-pending';
+        case 'Lost':
+          return 'status-error';
+        default:
+          return 'status-unknown';
+      }
+    };
+
+    const getPersistentVolumeClaimCapacity = (persistentVolumeClaim: KubernetesPersistentVolumeClaim): string => {
+      return persistentVolumeClaim.spec?.resources?.requests?.storage || 'Unknown';
+    };
+
+
+    onBeforeUnmount(() => {
+      document.removeEventListener('click', hideContextMenu);
+    });
+
+    return {
+
+      // State
+      showCreate,
+      searchQuery,
+      showMenu,
+      menuPosition,
+      cancelForm,
+      isSubmitting,
+
+      // Form
+      formError,
+      formName,
+      formNamespace,
+      formStorageClass,
+      formCapacity,
+      formCapacityUnit,
+      formAccessModes,
+
+      // Computed
+      filteredPersistentVolumeClaims,
+
+      // Methods
+      hideContextMenu,
+      showContextMenu,
+      isSelected,
+      selectPersistentVolumeClaim,
+      deletePersistentVolumeClaim,
+      createPersistentVolumeClaim,
+      getUniqueKey,
+      getPersistentVolumeClaimStorageClass,
+      getPersistentVolumeClaimAccessModes,
+      getPersistentVolumeClaimStatus,
+      getPersistentVolumeClaimStatusClass,
+      getPersistentVolumeClaimCapacity,
+      formatAge,
+    };
+  }
+});
+</script>
+
+<template>
+  <div class="persistentvolumeclaims-container">
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search persistent volume claims..."
+      />
+    </div>
+
+    <div v-if="filteredPersistentVolumeClaims.length === 0" class="no-persistentvolumeclaims">
+      <p v-if="searchQuery">No persistentvolumeclaims found matching "{{ searchQuery }}"</p>
+      <p v-else>No persistentvolumeclaims found.</p>
+    </div>
+
+    <div v-else class="table-scroll-container"> 
+      <table>
+        <thead>
+          <tr>
+            <th>Namespace</th>
+            <th>Name</th>
+            <th>Status</th>
+            <th>Volume</th>
+            <th>Capacity</th>
+            <th>Access Modes</th>
+            <th>Storage Class</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="persistentVolumeClaim in filteredPersistentVolumeClaims" 
+            :key="getUniqueKey(persistentVolumeClaim)"
+            :class="{ selected: isSelected(persistentVolumeClaim) }"
+            @click="selectPersistentVolumeClaim(persistentVolumeClaim)"
+            @contextmenu="showContextMenu($event, persistentVolumeClaim)"
+          >
+            <td>{{ persistentVolumeClaim.metadata.namespace }}</td>
+            <td>{{ persistentVolumeClaim.metadata.name }}</td>
+            <td :class="getPersistentVolumeClaimStatusClass(persistentVolumeClaim)">{{ getPersistentVolumeClaimStatus(persistentVolumeClaim) }}</td>
+            <td>{{ persistentVolumeClaim.spec?.volumeName || '-' }}</td>
+            <td>{{ getPersistentVolumeClaimCapacity(persistentVolumeClaim) }}</td>
+            <td>{{ getPersistentVolumeClaimAccessModes(persistentVolumeClaim) }}</td>
+            <td>{{ getPersistentVolumeClaimStorageClass(persistentVolumeClaim) }}</td>
+            <td>{{ formatAge(persistentVolumeClaim.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+      <div 
+        v-if="showMenu" 
+        class="context-menu" 
+        :style="{ top: menuPosition.y + 'px', left: menuPosition.x + 'px' }"
+        @click.stop
+      >
+        <div class="menu-item" @click="showCreate = true; hideContextMenu()">
+          Create
+        </div>
+        <div class="menu-item" @click="deletePersistentVolumeClaim">
+          Delete
+        </div>
+      </div>
+
+      <div v-if="showCreate" class="modal-overlay">
+        <div class="modal-content" @click.stop>
+          <div class="modal-header">
+            <h3>Create Persistent Volume Claim</h3>
+            <button class="close-button" @click="cancelForm">x</button>
+          </div>
+
+          <div class="modal-body">
+            <div v-if="formError" class="error-message">{{ formError }}</div>
+
+            <div class="form-group">
+              <label for="name">Name:</label>
+              <input id="name" v-model="formName" type="text" placeholder="test-claim-1" :disabled="isSubmitting">
+            </div>
+
+            <div class="form-group">
+              <label for="namespace">Namespace:</label>
+              <input id="namespace" v-model="formNamespace" type="text" placeholder="default" :disabled="isSubmitting">
+            </div>
+
+            <div class="form-group">
+              <label for="storageClass">Storage Class:</label>
+              <select id="storageClass" v-model="formStorageClass" :disabled="isSubmitting">
+                <option value="local-path">local-path</option>
+              </select>
+            </div>
+
+            <div class="form-group">
+              <label for="capacity">Capacity (Gi):</label>
+              <input id="capacity" v-model="formCapacity" type="number" min="1" placeholder="10" :disabled="isSubmitting"/>
+            </div>
+
+            <div class="form-group">
+              <label for="accessModes">Access Modes:</label>
+              <div>
+                <label class="checkbox-label">
+                  <input type="checkbox" v-model="formAccessModes" value="ReadWriteOnce" :disabled="isSubmitting" />
+                  ReadWriteOnce
+                </label>
+              </div>
+            </div>
+
+          </div>
+
+          <div class="modal-footer">
+            <button class="cancel-button" @click="cancelForm" :disabled="isSubmitting">Cancel</button>
+            <button class="create-button" @click="createPersistentVolumeClaim" :disabled="isSubmitting">{{ isSubmitting ? 'Creating...' : 'Create' }}</button>
+          </div>
+        </div>
+
+      </div>
+
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/CreateResource.css" scoped></style>
+<style src="@/assets/css/ListResource.css" scoped></style>
+<style src="@/assets/css/CreatePodMethodSelector.css" scoped></style>

diff --git a/frontend/src/components/PersistentVolumesCreateGuided.vue b/frontend/src/components/PersistentVolumesCreateGuided.vue
line changes: +296/-0
index 0000000..fc6d4ff
--- /dev/null
+++ b/frontend/src/components/PersistentVolumesCreateGuided.vue
@@ -0,0 +1,296 @@
+<!-- VolumesCreateGuided.vue -->
+<script lang="ts">
+import { defineComponent, ref, computed } from 'vue';
+import { SecretCreateOptions } from '../types/custom'
+
+export default defineComponent({
+  name: 'VolumesCreateGuided',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true
+    },
+    cluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: false,
+      default: null
+    }
+  },
+
+  emits: ['close', 'create-volume'],
+
+  setup(props, { emit }) {
+    const name = ref('');
+    const namespace = ref('default');
+    const isSubmitting = ref(false);
+    const error = ref('');
+    const volumeData = ref([{ key: '', value: '', showValue: false }]);
+    const accessModes = ref(['ReadWriteConce']);
+
+    const hostPath = ref({ path: '', type: ''});
+    const nfs = ref({ server: '', path: '', readOnly: false });
+    const volumeType = ref('');
+    const volumeTypes = [
+      {
+        value: 'hostPath',
+        label: 'HostPath',
+        category: 'local',
+        description: 'Mount a file or directory from the host name (development only)',
+      },
+      {
+        value: 'nfs',
+        label: 'NFS',
+        category: 'network',
+        description: 'Network File System for shared storage across multiple nodes',
+      },
+    ];
+
+    const isFormValid = computed(() => {
+      if (!name.value.trim()) return false;
+      if (!capacity.value || capacity.value <= 0) return false;
+      if (accessModes.value.length === 0)return false;
+      if (!volumeType.value) return false;
+
+      if (volumeType.value === 'hostPath') {
+        if (!hostPath.value.path.trim()) {
+          return false;
+        }
+      }
+
+      return true;
+    });
+
+    const getVolumeTypeLabel = () => {
+      const type = volumeTypes.find(t => t.value === volumeType.value);
+      return type ? type.label : '';
+    };
+
+    const selectVolumeType = (type: string) => {
+      volumeType.value = type;
+      error.value = '';
+    };
+
+    const addSecretData = () => {
+      volumeData.value.push({ key: '', value: '', showValue: false });
+    };
+
+    const removeSecretData = (index: number) => {
+      if (volumeData.value.length > 1) {
+        volumeData.value.splice(index, 1);
+      }
+    };
+
+    const resetForm = () => {
+      name.value = '';
+      namespace.value = 'default';
+      volumeData.value = [{ key: '', value: '' }];
+      error.value = '';
+      isSubmitting.value = false;
+    };
+
+    const closeModal = () => {
+      resetForm();
+      emit('close');
+    };
+
+    const toggleValueVisibility = (index: number) => {
+      volumeData.value[index].showValue = !volumeData.value[index].showValue;
+    };
+
+    const createPersistentVolume = () => {
+      if (!props.cluster) {
+        error.value = 'No cluster selected';
+        return;
+      }
+
+      if (!isFormValid.value) {
+        error.value = 'Please fill in all required fields';
+        return;
+      }
+
+      isSubmitting.value = true;
+
+      let volumeSource = {};
+      switch (volumeType.value) {
+        case 'hostPath':
+          volumeSource = {
+            hostPath: {
+              path: hostPath.value.path.trim(),
+              ...(hostPath.value.type && { type: hostPath.value.type })
+            }
+          };
+          break;
+      }
+
+      const spec: any = {
+        capacity: {
+          storage: `${capacity.value}${capacityUnit.value}`
+        },
+        accessModes: accessModes.value,
+        persistentVolumeReclaimPolicy: reclaimPolicy.value,
+        ...volumeSource
+      };
+
+      const stringData = {};
+      validData.forEach(item => {
+        stringData[item.key.trim()] = item.value;
+      });
+
+      const opts: SecretCreateOptions = {
+        context: props.cluster.contextName,
+        opts: {
+          apiVersion: 'v1',
+          kind: 'Secret',
+          metadata: {
+            name: name.value.trim(),
+            namespace: namespace.value.trim()
+          },
+          type: 'Opaque',
+          stringData: stringData
+        }
+      }
+      emit('create-volume', opts);
+      resetForm();
+      closeModal();
+    };
+
+    return {
+      name,
+      namespace,
+      volumeData,
+      isSubmitting,
+      error,
+      isFormValid,
+      addSecretData,
+      removeSecretData,
+      closeModal,
+      createSecret,
+      toggleValueVisibility,
+      getVolumeTypeLabel,
+      selectVolumeType,
+
+
+      volumeType,
+      volumeTypes,
+      hostPath,
+      nfs,
+      accessModes,
+
+    };
+  }
+});
+</script>
+
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content" @click.stop>
+      <div class="modal-header">
+        <h3>Create Persistent Volume</h3>
+        <button class="close-button" @click="closeModal">&times;</button>
+      </div>
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+        <div class="form-group">
+          <label for="name">Name:</label>
+          <input 
+            id="name" 
+            v-model="name" 
+            type="text" 
+            placeholder="my-persistent-volume"
+            :disabled="isSubmitting"
+          />
+        </div>
+        <div class="form-group">
+          <label for="storageClass">Storage Class:</label>
+          <input 
+            id="storageClass" 
+            v-model="storageClass" 
+            type="text" 
+            placeholder="standard"
+            :disabled="isSubmitting"
+          />
+        </div>
+        <div class="form-group">
+          <label for="capacity">Capacity (Gi):</label>
+          <div class="storage-input-group">
+            <input 
+              id="capacity" 
+              v-model="capacity" 
+              type="number" 
+              min="1"
+              placeholder="10"
+              :disabled="isSubmitting"
+            />
+          </div>
+        </div>
+        <div class="form-section">
+          <h4>Volume Type</h4>
+          <div class="volume-type-selector">
+            <div
+              v-for="type in volumeTypes"
+              :key="type.value"
+              class="volume-type-option"
+              :class="{ selected: volumeType === type.value }"
+              @click="selectVolumeType(type.value)"
+              >
+                <div class="type-header">
+                  <span class="type-name">{{ type.label }}</span>
+                  <span class="type-badge" :class="type.category">{{ type.category }}</span>
+                </div>
+                <p class="type-description">{{ type.description }}</p>
+            </div>
+          </div>
+        </div>
+        <div v-if="volumeType" class="form-section">
+          <h4>{{ getVolumeTypeLabel() }} Configuration</h4>
+          <div v-if="volumeType == 'hostPath'" class="volume-config">
+            <div class="form-group">
+              <label for="hostPath">Host Path:</label>
+              <input 
+                id="hostPath" 
+                v-model="hostPath.path" 
+                type="text" 
+                placeholder="/mnt/data"
+                :disabled="isSubmitting"
+              />
+              <small class="field-hint">Path on the host node</small>
+            </div>
+            <div class="form-group">
+              <label for="hostPathType">Path Type:</label>
+              <select id="hostPathType" v-model="hostPath.type" :disabled="isSubmitting">
+                <option value="">Default</option>
+                <option value="DirectoryOrCreate">DirectoryOrCreate</option>
+                <option value="Directory">Directory</option>
+                <option value="FileOrCreate">FileOrCreate</option>
+                <option value="File">File</option>
+                <option value="Socket">Socket</option>
+                <option value="CharDevice">CharDevice</option>
+                <option value="BlockDevice">BlockDevice</option>
+              </select>
+            </div>
+          </div>
+        </div>
+        <div class="modal-footer">
+          <button 
+            class="cancel-button" 
+            @click="closeModal"
+            :disabled="isSubmitting"
+          >
+            Cancel
+          </button>
+          <button 
+            class="create-button" 
+            @click="createPersistentVolume"
+            :disabled="!isFormValid || isSubmitting"
+          >
+            {{ isSubmitting ? 'Creating...' : 'Create' }}
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/CreateResource.css" scoped></style>
+<style src="@/assets/css/PersistentVolumes.css" scoped></style>

diff --git a/frontend/src/components/PersistentVolumesCreateMethodSelector.vue b/frontend/src/components/PersistentVolumesCreateMethodSelector.vue
line changes: +340/-0
index 0000000..77807bf
--- /dev/null
+++ b/frontend/src/components/PersistentVolumesCreateMethodSelector.vue
@@ -0,0 +1,340 @@
+<!-- PersistentVolumesCreateMethodSelector.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content method-selector" @click.stop>
+      <div class="modal-header">
+        <h3>Create Persistent Volume</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <p class="description">
+          Choose how you would like to create your Persistent Volume:
+        </p>
+
+        <div class="method-options">
+          <div 
+            class="method-option" 
+            @click="selectMethod('guided')"
+            :class="{ selected: selectedMethod === 'guided' }"
+          >
+            <div class="method-icon">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
+                <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
+              </svg>
+            </div>
+            <div class="method-content">
+              <h4>Guided Form</h4>
+              <p>Use a step-by-step form with validation and helpful hints. Recommended for beginners.</p>
+              <ul class="method-features">
+                <li>Form validation</li>
+                <li>Field descriptions</li>
+                <li>Storage type selection</li>
+                <li>Access mode guidance</li>
+              </ul>
+            </div>
+          </div>
+
+          <div 
+            class="method-option" 
+            @click="selectMethod('yaml')"
+            :class="{ selected: selectedMethod === 'yaml' }"
+          >
+            <div class="method-icon">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
+                <path d="M14,17H7V15H14M17,13H7V11H17M17,9H7V7H17M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z"/>
+              </svg>
+            </div>
+            <div class="method-content">
+              <h4>YAML Editor</h4>
+              <p>Write or paste YAML directly. Provides full control and flexibility for advanced users.</p>
+              <ul class="method-features">
+                <li>Full YAML control</li>
+                <li>Template loading</li>
+                <li>Syntax validation</li>
+                <li>Copy/paste support</li>
+              </ul>
+            </div>
+          </div>
+        </div>
+
+        <div class="volume-types-info">
+          <h4>Supported Volume Types:</h4>
+          <div class="volume-types">
+            <span class="volume-type">HostPath</span>
+            <span class="volume-type">NFS</span>
+            <span class="volume-type">AWS EBS</span>
+            <span class="volume-type">Azure Disk</span>
+            <span class="volume-type">GCE PD</span>
+            <span class="volume-type">iSCSI</span>
+            <span class="volume-type">Local</span>
+            <span class="volume-type">CSI</span>
+          </div>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+        >
+          Cancel
+        </button>
+        <button 
+          class="continue-button" 
+          @click="continueWithMethod"
+          :disabled="!selectedMethod"
+        >
+          Continue
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+
+export default defineComponent({
+  name: 'PersistentVolumesCreateMethodSelector',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true
+    }
+  },
+
+  emits: ['close', 'method-selected'],
+
+  setup(props, { emit }) {
+    const selectedMethod = ref<string>('');
+
+    const selectMethod = (method: string) => {
+      selectedMethod.value = method;
+    };
+
+    const closeModal = () => {
+      selectedMethod.value = '';
+      emit('close');
+    };
+
+    const continueWithMethod = () => {
+      if (selectedMethod.value) {
+        emit('method-selected', selectedMethod.value);
+        selectedMethod.value = '';
+      }
+    };
+
+    return {
+      selectedMethod,
+      selectMethod,
+      closeModal,
+      continueWithMethod
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/MenuDialog.css" scoped></style>
+
+<style scoped>
+.method-selector {
+  max-width: 700px;
+  width: 90vw;
+}
+
+.description {
+  color: #ccc;
+  margin-bottom: 1.5rem;
+  text-align: center;
+  font-size: 1rem;
+}
+
+.method-options {
+  display: flex;
+  flex-direction: column;
+  gap: 1rem;
+  margin-bottom: 2rem;
+}
+
+.method-option {
+  display: flex;
+  align-items: flex-start;
+  gap: 1rem;
+  padding: 1.5rem;
+  border: 2px solid #444;
+  border-radius: 8px;
+  background-color: #2d2d2d;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.method-option:hover {
+  border-color: #007acc;
+  background-color: #333;
+}
+
+.method-option.selected {
+  border-color: #007acc;
+  background-color: #1a3a5c;
+}
+
+.method-icon {
+  flex-shrink: 0;
+  width: 48px;
+  height: 48px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: #007acc;
+  border-radius: 8px;
+  color: white;
+}
+
+.method-option.selected .method-icon {
+  background-color: #0099ff;
+}
+
+.method-content {
+  flex: 1;
+}
+
+.method-content h4 {
+  margin: 0 0 0.5rem 0;
+  color: #ffffff;
+  font-size: 1.2rem;
+}
+
+.method-content p {
+  margin: 0 0 1rem 0;
+  color: #ccc;
+  line-height: 1.4;
+}
+
+.method-features {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 0.5rem;
+}
+
+.method-features li {
+  background-color: #444;
+  color: #ccc;
+  padding: 0.25rem 0.5rem;
+  border-radius: 4px;
+  font-size: 0.85rem;
+}
+
+.method-option.selected .method-features li {
+  background-color: #007acc;
+  color: white;
+}
+
+.volume-types-info {
+  background-color: #333;
+  border: 1px solid #444;
+  border-radius: 6px;
+  padding: 1rem;
+  margin-bottom: 1rem;
+}
+
+.volume-types-info h4 {
+  margin: 0 0 0.75rem 0;
+  color: #ffffff;
+  font-size: 1rem;
+}
+
+.volume-types {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 0.5rem;
+}
+
+.volume-type {
+  background-color: #007acc;
+  color: white;
+  padding: 0.25rem 0.75rem;
+  border-radius: 12px;
+  font-size: 0.85rem;
+  font-weight: 500;
+}
+
+.modal-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 0.5rem;
+  padding: 1rem 1.5rem;
+  border-top: 1px solid #444;
+}
+
+.cancel-button {
+  padding: 0.5rem 1rem;
+  border: 1px solid #666;
+  background-color: transparent;
+  color: #ccc;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.cancel-button:hover {
+  background-color: #444;
+  border-color: #777;
+}
+
+.continue-button {
+  padding: 0.5rem 1rem;
+  border: 1px solid #007acc;
+  background-color: #007acc;
+  color: white;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.continue-button:hover:not(:disabled) {
+  background-color: #005a9e;
+  border-color: #005a9e;
+}
+
+.continue-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+/* Responsive design */
+@media (max-width: 768px) {
+  .method-selector {
+    width: 95vw;
+    max-width: none;
+  }
+
+  .method-options {
+    gap: 0.75rem;
+  }
+
+  .method-option {
+    padding: 1rem;
+    flex-direction: column;
+    text-align: center;
+  }
+
+  .method-icon {
+    align-self: center;
+    margin-bottom: 0.5rem;
+  }
+
+  .method-features {
+    justify-content: center;
+  }
+
+  .volume-types {
+    justify-content: center;
+  }
+}
+</style>
+

diff --git a/frontend/src/components/PersistentVolumesList.vue b/frontend/src/components/PersistentVolumesList.vue
line changes: +337/-0
index 0000000..2b188b5
--- /dev/null
+++ b/frontend/src/components/PersistentVolumesList.vue
@@ -0,0 +1,337 @@
+<!-- PersistentVolumeList.vue -->
+<script lang="ts">
+import { defineComponent, ref, computed, PropType, onBeforeUnmount } from 'vue';
+import { KubernetesCluster, KubernetesPersistentVolume, KubernetesPersistentVolumeClaim } from '../types/kubernetes';
+import { formatAge } from '../lib/format'; 
+import SearchBar from './SearchBar.vue';
+
+export default defineComponent({
+  name: 'PersistentVolumeList',
+
+  components: {
+    SearchBar
+  },
+
+  props: {
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster>,
+      required: false,
+      default: null
+    },
+    persistentVolumes: {
+      type: Array as PropType<KubernetesPersistentVolume[]>,
+      required: false,
+      default: () => []
+    },
+    persistentVolumeClaims: {
+      type: Array as PropType<KubernetesPersistentVolumeClaim[]>,
+      required: false,
+      default: () => []
+    }
+  },
+
+  emits: ['persistentVolume-selected', 'delete-persistentvolume'],
+
+  setup(props, { emit }) {
+    // State
+    const searchQuery = ref('');
+    const selectedPersistentVolume = ref<KubernetesPersistentVolume | null>(null);
+    const selectedContextPersistentVolume = ref<KubernetesPersistentVolume | null>(null);
+    const showMenu = ref(false);
+    const menuPosition = ref({ x: 0, y: 0 });
+
+    // Computed properties
+    const filteredPersistentVolumes = computed(() => {
+      if (!searchQuery.value) {
+        return props.persistentVolumes;
+      }
+
+      const query = searchQuery.value.toLowerCase();
+
+      const nameSpecificMatch = query.match(/^name:(.+)$/);
+      if (nameSpecificMatch) {
+        const nameQuery = nameSpecificMatch[1].trim();
+        return props.persistentVolumes.filter(persistentVolume => {
+          const name = persistentVolume.metadata.name.toLowerCase();
+          return name.includes(nameQuery);
+        });
+      }
+
+      const statusSpecificMatch = query.match(/^status:(.+)$/);
+      if (statusSpecificMatch) {
+        const statusQuery = statusSpecificMatch[1].trim();
+        return props.persistentVolumes.filter(persistentVolume => {
+          const status = persistentVolume.status?.phase?.toLowerCase() || '';
+          return status.includes(statusQuery);
+        });
+      }
+
+      const claimSpecificMatch = query.match(/^claim:(.+)$/);
+      if (claimSpecificMatch) {
+        const claimQuery = claimSpecificMatch[1].trim();
+        return props.persistentVolumes.filter(persistentVolume => {
+          const claimName = persistentVolume.spec?.claimRef?.name?.toLowerCase() || '';
+          return claimName.includes(claimQuery);
+        });
+      }
+
+      const storageClassSpecificMatch = query.match(/^storageclass:(.+)$/);
+      if (storageClassSpecificMatch) {
+        const storageClassQuery = storageClassSpecificMatch[1].trim();
+        return props.persistentVolumes.filter(persistentVolume => {
+          const storageClass = persistentVolume.spec?.storageClassName?.toLowerCase() || '';
+          return storageClass.includes(storageClassQuery);
+        });
+      }
+
+      const labelSpecificMatch = query.match(/^label:(.+)$/);
+      if (labelSpecificMatch) {
+        const labelQuery = labelSpecificMatch[1].trim();
+        return props.persistentVolumes.filter(persistentVolume => {
+          const labels = persistentVolume.metadata.labels || {};
+          for (const key in labels) {
+            const value = labels[key].toLowerCase();
+            const keyLower = key.toLowerCase();
+
+            // Check for key=value format
+            if (labelQuery.includes('=')) {
+              const [queryKey, queryValue] = labelQuery.split('=');
+              if (keyLower === queryKey.trim().toLowerCase() && 
+                  value.includes(queryValue.trim().toLowerCase())) {
+                return true;
+              }
+            } 
+            // Check for just key
+            else if (keyLower.includes(labelQuery)) {
+              return true;
+            }
+          }
+          return false;
+        });
+      }
+
+      // Default behavior: search only in persistent volume names
+      return props.persistentVolumes.filter(persistentVolume => {
+        const name = persistentVolume.metadata.name.toLowerCase();
+        return name.includes(query);
+      });
+    });
+
+    // Methods
+    const hideContextMenu = () => {
+      showMenu.value = false;
+      document.removeEventListener('click', hideContextMenu);
+    };
+
+    const showContextMenu = (event: MouseEvent, persistentVolume: KubernetesPersistentVolume) => {
+      event.preventDefault();
+
+      menuPosition.value = {
+        x: event.clientX,
+        y: event.clientY
+      };
+
+      selectedContextPersistentVolume.value = persistentVolume;
+      selectPersistentVolume(persistentVolume);
+      showMenu.value = true;
+
+      setTimeout(() => {
+        document.addEventListener('click', hideContextMenu);
+      }, 0);
+    };
+
+    const isSelected = (persistentVolume: KubernetesPersistentVolume): boolean => {
+      if (!selectedPersistentVolume.value) return false;
+      return selectedPersistentVolume.value.metadata.name === persistentVolume.metadata.name &&
+        selectedPersistentVolume.value.metadata.uid === persistentVolume.metadata.uid;
+    };
+
+    const selectPersistentVolume = (persistentVolume: KubernetesPersistentVolume): void => {
+      try {
+        const payload = { ...persistentVolume };
+        if (!payload.kind) {
+          payload.kind = 'PersistentVolume';
+        }
+        selectedPersistentVolume.value = payload;
+        emit('persistentVolume-selected', payload);
+      } catch (error) {
+        alert("Failed to select Persistent Volume:", error);
+      }
+    };
+
+    const deletePersistentVolume = (): void => {
+      if (selectedContextPersistentVolume.value && props.selectedCluster) {
+        try {
+          emit('delete-persistentvolume', {
+            cluster: props.selectedCluster,
+            definition: selectedContextPersistentVolume.value
+          });
+        } catch (error) {
+          alert(error);
+        }
+      }
+      hideContextMenu();
+    };
+
+    const getUniqueKey = (persistentVolume: KubernetesPersistentVolume): string => {
+      return `${persistentVolume.metadata.name}-${persistentVolume.metadata.uid || ''}`;
+    };
+
+    const getPersistentVolumeStorageClass = (persistentVolume: KubernetesPersistentVolume): string => {
+      return persistentVolume.spec?.storageClassName || '-';
+    };
+
+    const getPersistentVolumeAccessModes = (persistentVolume: KubernetesPersistentVolume): string => {
+      const accessModes = persistentVolume.spec?.accessModes?.join(', ') || 'Unknown';
+      switch (accessModes) {
+        case "ReadWriteOnce":
+          return "RWO";
+        case "ReadOnlyMany":
+          return "ROX";
+        case "ReadWriteMany":
+          return "RWX";
+        case "ReadWriteOncePod":
+          return "RWOP";
+        default:
+          return accessModes;
+      }
+    };
+
+    const getPersistentVolumeStatus = (persistentVolume: KubernetesPersistentVolume): string => {
+      return persistentVolume.status?.phase || 'Unknown';
+    };
+
+    const getPersistentVolumeStatusClass = (persistentVolume: KubernetesPersistentVolume): string => {
+      const status = getPersistentVolumeStatus(persistentVolume);
+      switch (status) {
+        case 'Available':
+          return 'status-available';
+        case 'Bound':
+          return 'status-active';
+        case 'Released':
+          return 'status-warning';
+        case 'Failed':
+          return 'status-error';
+        default:
+          return 'status-unknown';
+      }
+    };
+
+    const getPersistentVolumeCapacity = (persistentVolume: KubernetesPersistentVolume): string => {
+      return persistentVolume.spec?.capacity?.storage || 'Unknown';
+    };
+
+    const getPersistentVolumeClaim = (persistentVolume: KubernetesPersistentVolume): string => {
+      const claimRef = persistentVolume.spec?.claimRef;
+      if (claimRef) {
+        return `${claimRef.namespace}/${claimRef.name}`;
+      }
+      return '-';
+    };
+
+    const getPersistentVolumeReclaimPolicy = (persistentVolume: KubernetesPersistentVolume): string => {
+      return persistentVolume.spec?.persistentVolumeReclaimPolicy || 'Retain';
+    };
+
+    onBeforeUnmount(() => {
+      document.removeEventListener('click', hideContextMenu);
+    });
+
+    // Return all refs, computed properties, and methods
+    return {
+      // State
+      searchQuery,
+      selectedPersistentVolume,
+      selectedContextPersistentVolume,
+      showMenu,
+      menuPosition,
+
+      // Props
+      persistentVolumeClaims: props.persistentVolumeClaims,
+
+      // Computed
+      filteredPersistentVolumes,
+
+      // Methods
+      hideContextMenu,
+      showContextMenu,
+      isSelected,
+      selectPersistentVolume,
+      deletePersistentVolume,
+      getUniqueKey,
+      getPersistentVolumeStorageClass,
+      getPersistentVolumeAccessModes,
+      getPersistentVolumeStatus,
+      getPersistentVolumeStatusClass,
+      getPersistentVolumeCapacity,
+      getPersistentVolumeClaim,
+      getPersistentVolumeReclaimPolicy,
+      formatAge
+    };
+  }
+});
+</script>
+
+<template>
+  <div class="persistentvolumes-container">
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search persistent volumes..."
+      />
+    </div>
+
+    <div v-if="filteredPersistentVolumes.length === 0" class="no-persistentvolumes">
+      <p v-if="searchQuery">No persistent volumes found matching "{{ searchQuery }}"</p>
+      <p v-else>No persistent volumes found.</p>
+    </div>
+
+    <div v-else class="table-scroll-container"> 
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Status</th>
+            <th>Claim</th>
+            <th>Capacity</th>
+            <th>Access Modes</th>
+            <th>Reclaim Policy</th>
+            <th>Storage Class</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="persistentVolume in filteredPersistentVolumes" 
+            :key="getUniqueKey(persistentVolume)"
+            :class="{ selected: isSelected(persistentVolume) }"
+            @click="selectPersistentVolume(persistentVolume)"
+            @contextmenu="showContextMenu($event, persistentVolume)"
+          >
+            <td>{{ persistentVolume.metadata.name }}</td>
+            <td :class="getPersistentVolumeStatusClass(persistentVolume)">{{ getPersistentVolumeStatus(persistentVolume) }}</td>
+            <td>{{ getPersistentVolumeClaim(persistentVolume) }}</td>
+            <td>{{ getPersistentVolumeCapacity(persistentVolume) }}</td>
+            <td>{{ getPersistentVolumeAccessModes(persistentVolume) }}</td>
+            <td>{{ getPersistentVolumeReclaimPolicy(persistentVolume) }}</td>
+            <td>{{ getPersistentVolumeStorageClass(persistentVolume) }}</td>
+            <td>{{ formatAge(persistentVolume.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+      <div 
+        v-if="showMenu" 
+        class="context-menu" 
+        :style="{ top: menuPosition.y + 'px', left: menuPosition.x + 'px' }"
+        @click.stop
+      >
+        <div class="menu-item" @click="deletePersistentVolume">
+          Delete
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/ListResource.css" scoped></style>

diff --git a/frontend/src/components/PodsList.vue b/frontend/src/components/PodsList.vue
line changes: +388/-0
index 0000000..be9b390
--- /dev/null
+++ b/frontend/src/components/PodsList.vue
@@ -0,0 +1,388 @@
+<script lang="ts">
+import { defineComponent, PropType, ref, computed } from 'vue';
+import { KubernetesCluster, KubernetesPod } from '../types/kubernetes';
+import { formatAge } from '../lib/format';
+import SearchBar from './SearchBar.vue';
+import CreatePodGuided from './CreatePodGuided.vue';
+import CreatePodMethodSelector from './CreatePodMethodSelector.vue';
+import CreatePodYaml from './CreatePodYaml.vue';
+import EditPodYaml from './EditPodYaml.vue';
+
+export default defineComponent({
+  name: 'PodsList',
+
+  components: {
+    SearchBar,
+    CreatePodGuided,
+    CreatePodMethodSelector,
+    CreatePodYaml,
+    EditPodYaml
+  },
+
+  props: {
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: false,
+      default: null
+    },
+    pods: {
+      type: Array as PropType<KubernetesPod[]>,
+      required: false,
+      default: () => []
+    }
+  },
+
+  emits: ['pod-selected', 'delete-pod', 'create-pod'],
+
+  setup(props) {
+    // Search functionality
+    const searchQuery = ref('');
+
+    const filteredPods = computed(() => {
+      if (!searchQuery.value) {
+        return props.pods;
+      }
+
+      const query = searchQuery.value.toLowerCase();
+
+      // Check if the query is in the format "name:pod-name"
+      const nameSpecificMatch = query.match(/^name:(.+)$/);
+      if (nameSpecificMatch) {
+        const nameQuery = nameSpecificMatch[1].trim();
+        return props.pods.filter(pod => {
+          const name = pod.metadata.name.toLowerCase();
+          return name.includes(nameQuery);
+        });
+      }
+
+      // Check if the query is in the format "namespace:namespace-name"
+      const namespaceSpecificMatch = query.match(/^namespace:(.+)$/);
+      if (namespaceSpecificMatch) {
+        const namespaceQuery = namespaceSpecificMatch[1].trim();
+        return props.pods.filter(pod => {
+          const namespace = pod.metadata.namespace?.toLowerCase() || '';
+          return namespace.includes(namespaceQuery);
+        });
+      }
+
+      // Check if the query is in the format "label:key=value" or "label:key"
+      const labelSpecificMatch = query.match(/^label:(.+)$/);
+      if (labelSpecificMatch) {
+        const labelQuery = labelSpecificMatch[1].trim();
+        return props.pods.filter(pod => {
+          const labels = pod.metadata.labels || {};
+          for (const key in labels) {
+            const value = labels[key].toLowerCase();
+            const keyLower = key.toLowerCase();
+
+            // Check for key=value format
+            if (labelQuery.includes('=')) {
+              const [queryKey, queryValue] = labelQuery.split('=');
+              if (keyLower === queryKey.trim().toLowerCase() && 
+                  value.includes(queryValue.trim().toLowerCase())) {
+                return true;
+              }
+            } 
+            // Check for just key
+            else if (keyLower.includes(labelQuery)) {
+              return true;
+            }
+          }
+          return false;
+        });
+      }
+
+      const statusSpecificMatch = query.match(/^status:(.+)$/);
+      if (statusSpecificMatch) {
+        const statusQuery = statusSpecificMatch[1].trim();
+        return props.pods.filter(pod => {
+          const status = pod.status.phase.toLowerCase();
+          return status.includes(statusQuery);
+        });
+      }
+
+      // Default behavior: search only in pod names
+      return props.pods.filter(pod => {
+        const name = pod.metadata.name.toLowerCase();
+        return name.includes(query);
+      });
+    });
+
+
+    return {
+      searchQuery,
+      filteredPods,
+      formatAge
+    };
+  },
+
+  data() {
+    return {
+      selectedPod: null as KubernetesPod | null,
+      showMenu: false,
+      menuPosition: { x: 0, y: 0 },
+      selectedContextPod: null as KubernetesPod | null,
+      showMethodSelector: false,
+      showCreateGuided: false,
+      showCreateYaml: false,
+      showEditYaml: false,
+      currentNamespace: 'default'
+    };
+  },
+
+  beforeUnmount() {
+    document.removeEventListener('click', this.hideContextMenu);
+  },
+
+  watch: {
+    pods: {
+      handler(newPods) {
+        console.log('Pods changed in PodsList:', newPods);
+      },
+      deep: true
+    }
+  },
+
+  methods: {
+
+    editPod(): void {
+      if (this.selectedContextPod) {
+        this.showEditYaml = true;
+      }
+      this.hideContextMenu();
+    },
+
+    handlePodUpdated(result: any): void {
+      if (this.selectedCluster) {
+        this.$emit('refresh-pods', {
+          cluster: this.selectedCluster
+        });
+      }
+    },
+
+    handleMethodSelected(method: string): void {
+      this.showMethodSelector = false;
+
+      if (method === 'guided') {
+        this.showCreateGuided = true;
+      } else if (method === 'yaml') {
+        this.showCreateYaml = true;
+      }
+    },
+
+    handleCreatePod({podDefinition, yamlContent, isYaml}: { podDefinition?: any, yamlContent?: string, isYaml?: boolean}) {
+      if (!this.selectedCluster) {
+        return
+      }
+
+      let opts = {
+        cluster: this.selectedCluster,
+        isYaml: isYaml,
+      }
+
+      if (yamlContent) {
+        opts.yamlContent = yamlContent
+        this.$emit('create-pod', opts)
+      } else if (podDefinition) {
+        opts.podDefinition = podDefinition
+        this.$emit('create-pod', opts)
+      } else {
+        alert(`Unable to emit create-pod`)
+      }
+
+      this.showCreateGuided = false;
+      this.showCreateYaml = false;
+    },
+
+    selectPod(pod: KubernetesPod): void {
+      const podToEmit = { ...pod };
+      if (!podToEmit.kind) {
+        podToEmit.kind = 'Pod';
+      }
+      this.selectedPod = podToEmit;
+      this.$emit('pod-selected', podToEmit);
+    },
+
+    isSelected(pod: KubernetesPod): boolean {
+      return this.selectedPod?.metadata.name === pod.metadata.name;
+    },
+
+    getPodStatus(pod: KubernetesPod): string {
+      if (pod.status && pod.status.phase) {
+        return pod.status.phase;
+      }
+      return 'Unknown';
+    },
+
+    getPodStatusClass(pod: KubernetesPod): string {
+      const status = this.getPodStatus(pod);
+      switch (status) {
+        case 'Running':
+          return 'status-running';
+        case 'Pending':
+          return 'status-pending';
+        case 'Succeeded':
+          return 'status-succeeded';
+        case 'Failed':
+          return 'status-failed';
+        default:
+          return 'status-unknown';
+      }
+    },
+
+    // Context menu methods
+    showContextMenu(event: MouseEvent, pod: KubernetesPod): void {
+      // Prevent the default context menu
+      event.preventDefault();
+
+      // Position the menu
+      this.menuPosition = {
+        x: event.clientX,
+        y: event.clientY
+      };
+
+      // Store the pod that was right-clicked
+      this.selectedContextPod = pod;
+
+      // Select the pod when right-clicking
+      this.selectPod(pod);
+
+      // Show the menu
+      this.showMenu = true;
+
+      // Add a click event listener to hide the menu when clicking outside
+      // Use setTimeout to avoid the current click event from immediately closing the menu
+      setTimeout(() => {
+        document.addEventListener('click', this.hideContextMenu);
+      }, 0);
+    },
+
+    hideContextMenu(): void {
+      this.showMenu = false;
+      document.removeEventListener('click', this.hideContextMenu);
+    },
+
+    deletePod(): void {
+      if (this.selectedContextPod && this.selectedCluster) {
+        this.$emit('delete-pod', {
+          cluster: this.selectedCluster,
+          pod: this.selectedContextPod
+        });
+      }
+      this.hideContextMenu();
+    },
+
+    createPod(): void {
+      this.currentNamespace = this.selectedContextPod?.metadata.namespace || 'default';
+      this.showMethodSelector = true;
+      this.hideContextMenu();
+    },
+
+    getUniqueKey(pod: KubernetesPod): string {
+      const namespace = pod.metadata.namespace || 'default';
+      return `${namespace}-${pod.metadata.name}-${pod.metadata.uid || ''}`;
+    }
+  }
+});
+</script>
+
+<template>
+  <div class="pods-container">
+    <!-- Fixed Search Bar -->
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search pods..."
+      />
+    </div>
+
+    <div v-if="filteredPods.length === 0" class="no-pods">
+      <p v-if="searchQuery">No pods found matching "{{ searchQuery }}"</p>
+      <p v-else>No pods found.</p>
+    </div>
+
+    <div v-else class="table-scroll-container">
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Namespace</th>
+            <th>Ready</th>
+            <th>Status</th>
+            <th>Restarts</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="pod in filteredPods" 
+            :key="getUniqueKey(pod)"
+            :class="{ selected: isSelected(pod) }"
+            @click="selectPod(pod)"
+            @contextmenu="showContextMenu($event, pod)"
+          >
+            <td>{{ pod.metadata.name }}</td>
+            <td>{{ pod.metadata.namespace }}</td>
+            <td>{{ `${pod.status && pod.status.containerStatuses ? pod.status.containerStatuses.filter(cs => cs.ready).length : 0}/${pod.spec && pod.spec.containers ? pod.spec.containers.length : 0}` }}</td>
+            <td :class="getPodStatusClass(pod)">{{ getPodStatus(pod) }}</td>
+            <td>{{ pod.status && pod.status.containerStatuses ? pod.status.containerStatuses.reduce((sum, cs) => sum + cs.restartCount, 0) : 0 }}</td>
+            <td>{{ formatAge(pod.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+      <div 
+        v-if="showMenu" 
+        class="context-menu" 
+        :style="{ top: menuPosition.y + 'px', left: menuPosition.x + 'px' }"
+        @click.stop
+      >
+        <div class="menu-item" @click="createPod">
+          Create
+        </div>
+        <div class="menu-item" @click="editPod">
+          Edit
+        </div>
+        <div class="menu-item" @click="deletePod">
+          Delete
+        </div>
+      </div>
+    </div>
+
+    <!-- Method selector dialog -->
+    <CreatePodMethodSelector 
+      :show="showMethodSelector" 
+      :namespace="currentNamespace"
+      @close="showMethodSelector = false"
+      @method-selected="handleMethodSelected"
+    />
+
+    <!-- Guided creation form -->
+    <CreatePodGuided 
+      :show="showCreateGuided" 
+      :namespace="currentNamespace"
+      @close="showCreateGuided = false"
+      @create-pod="handleCreatePod"
+    />
+
+    <!-- YAML creation form -->
+    <CreatePodYaml 
+      :show="showCreateYaml" 
+      :namespace="currentNamespace"
+      @close="showCreateYaml = false"
+      @create-pod="handleCreatePod"
+    />
+
+    <EditPodYaml
+      :show="showEditYaml"
+      :pod="selectedContextPod"
+      :cluster="selectedCluster"
+      @close="showEditYaml = false"
+      @pod-updated="handlePodUpdated"
+    />
+
+  </div>
+</template>
+
+<style src="@/assets/css/PodsList.css" scoped></style>
+

diff --git a/frontend/src/components/SearchBar.vue b/frontend/src/components/SearchBar.vue
line changes: +52/-0
index 0000000..efbb3c8
--- /dev/null
+++ b/frontend/src/components/SearchBar.vue
@@ -0,0 +1,52 @@
+<script lang="ts">
+import { defineComponent } from 'vue';
+
+export default defineComponent({
+  name: 'SearchBar',
+  props: {
+    value: {
+      type: String,
+      default: ''
+    },
+    placeholder: {
+      type: String,
+      default: 'Search...'
+    }
+  },
+  emits: ['update:value'],
+  methods: {
+    updateSearch(event: Event) {
+      const target = event.target as HTMLInputElement;
+      this.$emit('update:value', target.value);
+    },
+    clearSearch() {
+      this.$emit('update:value', '');
+    }
+  }
+});
+</script>
+
+<template>
+  <div class="search-container">
+    <div class="search-input-wrapper">
+      <input
+        type="text"
+        class="search-input"
+        :placeholder="placeholder"
+        :value="value"
+        @input="updateSearch"
+      />
+      <button 
+        v-if="value" 
+        class="clear-button" 
+        @click="clearSearch"
+        aria-label="Clear search"
+      >
+        ×
+      </button>
+    </div>
+  </div>
+</template>
+
+
+<style src="@/assets/css/SearchBar.css" scoped></style>

diff --git a/frontend/src/components/SecretsCreateGuided.vue b/frontend/src/components/SecretsCreateGuided.vue
line changes: +236/-0
index 0000000..d19eb36
--- /dev/null
+++ b/frontend/src/components/SecretsCreateGuided.vue
@@ -0,0 +1,236 @@
+<!-- SecretsCreateGuided.vue -->
+<script lang="ts">
+import { defineComponent, ref, computed } from 'vue';
+import { SecretCreateOptions } from '../types/custom'
+
+export default defineComponent({
+  name: 'SecretsCreateGuided',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true
+    },
+    cluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      required: false,
+      default: null
+    }
+  },
+
+  emits: ['close', 'create-secret'],
+
+  setup(props, { emit }) {
+    const name = ref('');
+    const namespace = ref('default');
+    const isSubmitting = ref(false);
+    const error = ref('');
+    const secretData = ref([{ key: '', value: '', showValue: false }]);
+
+    const isFormValid = computed(() => {
+      if (!name.value.trim()) return false;
+      if (!namespace.value.trim()) return false;
+
+      // Check if at least one key-value pair is valid
+      return secretData.value.some(item => 
+        item.key.trim() !== '' && item.value.trim() !== ''
+      );
+    });
+
+    const addSecretData = () => {
+      secretData.value.push({ key: '', value: '', showValue: false });
+    };
+
+    const removeSecretData = (index: number) => {
+      if (secretData.value.length > 1) {
+        secretData.value.splice(index, 1);
+      }
+    };
+
+    const resetForm = () => {
+      name.value = '';
+      namespace.value = 'default';
+      secretData.value = [{ key: '', value: '' }];
+      error.value = '';
+      isSubmitting.value = false;
+    };
+
+    const closeModal = () => {
+      resetForm();
+      emit('close');
+    };
+
+    const toggleValueVisibility = (index: number) => {
+      secretData.value[index].showValue = !secretData.value[index].showValue;
+    };
+
+
+    const createSecret = () => {
+      if (!props.cluster) {
+        error.value = 'No cluster selected';
+        return;
+      }
+
+      if (!name.value.trim()) {
+        error.value = 'Name is required';
+        return;
+      }
+
+      if (!namespace.value.trim()) {
+        error.value = 'Namespace is required';
+        return;
+      }
+
+      // Check if at least one key-value pair is valid
+      const validData = secretData.value.filter(
+        item => item.key.trim() !== '' && item.value.trim() !== ''
+      );
+
+      if (validData.length === 0) {
+        error.value = 'At least one key-value pair is required';
+        return;
+      }
+
+      isSubmitting.value = true;
+
+      const stringData = {};
+      validData.forEach(item => {
+        stringData[item.key.trim()] = item.value;
+      });
+
+      const opts: SecretCreateOptions = {
+        context: props.cluster.contextName,
+        opts: {
+          apiVersion: 'v1',
+          kind: 'Secret',
+          metadata: {
+            name: name.value.trim(),
+            namespace: namespace.value.trim()
+          },
+          type: 'Opaque',
+          stringData: stringData
+        }
+      }
+      emit('create-secret', opts);
+      resetForm();
+      closeModal();
+    };
+
+    return {
+      name,
+      namespace,
+      secretData,
+      isSubmitting,
+      error,
+      isFormValid,
+      addSecretData,
+      removeSecretData,
+      closeModal,
+      createSecret,
+      toggleValueVisibility
+    };
+  }
+});
+</script>
+
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content" @click.stop>
+      <div class="modal-header">
+        <h3>Create Secret</h3>
+        <button class="close-button" @click="closeModal">&times;</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div class="form-group">
+          <label for="name">Name:</label>
+          <input 
+            id="name" 
+            v-model="name" 
+            type="text" 
+            placeholder="test-secret-1"
+            :disabled="isSubmitting"
+          />
+        </div>
+
+        <div class="form-group">
+          <label for="namespace">Namespace:</label>
+          <input 
+            id="namespace" 
+            v-model="namespace" 
+            type="text" 
+            placeholder="default"
+            :disabled="isSubmitting"
+          />
+        </div>
+
+        <div class="form-section">
+          <h4>Secret Data</h4>
+          <div 
+            v-for="(item, index) in secretData" 
+            :key="index" 
+            class="secret-data-row"
+          >
+            <div class="form-group key-field">
+              <label :for="`key-${index}`">Key:</label>
+              <input 
+                :id="`key-${index}`" 
+                v-model="item.key" 
+                type="text" 
+                placeholder="username"
+                :disabled="isSubmitting"
+              />
+            </div>
+            <div class="form-group value-field">
+              <label :for="`value-${index}`">Value:</label>
+              <input 
+                :id="`value-${index}`" 
+                v-model="item.value" 
+                type="password" 
+                placeholder="••••••••"
+                :disabled="isSubmitting"
+              />
+            </div>
+            <button 
+              class="remove-button" 
+              @click="removeSecretData(index)"
+              :disabled="isSubmitting || secretData.length <= 1"
+            >
+              &times;
+            </button>
+          </div>
+
+          <!-- Single add button outside the v-for loop -->
+          <button 
+            class="add-button" 
+            @click="addSecretData"
+            :disabled="isSubmitting"
+          >
+            + Add Another Key-Value Pair
+          </button>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="create-button" 
+          @click="createSecret"
+          :disabled="isSubmitting"
+        >
+          {{ isSubmitting ? 'Creating...' : 'Create' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/CreateResource.css" scoped></style>

diff --git a/frontend/src/components/SecretsCreateMethodSelector.vue b/frontend/src/components/SecretsCreateMethodSelector.vue
line changes: +86/-0
index 0000000..8b062b8
--- /dev/null
+++ b/frontend/src/components/SecretsCreateMethodSelector.vue
@@ -0,0 +1,86 @@
+<!-- SecretsCreateMethodSelector.vue  -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content" @click.stop>
+      <div class="modal-header">
+        <h3>Create Secret</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div class="method-option" @click="selectedMethod = 'guided'">
+          <div class="radio-container">
+            <div class="radio-button">
+              <div class="radio-outer">
+                <div class="radio-inner" v-if="selectedMethod === 'guided'"></div>
+              </div>
+            </div>
+            <div class="method-text">
+              <div class="method-label">Guided Creation</div>
+            </div>
+          </div>
+        </div>
+
+        <div class="method-option" @click="selectedMethod = 'yaml'">
+          <div class="radio-container">
+            <div class="radio-button">
+              <div class="radio-outer">
+                <div class="radio-inner" v-if="selectedMethod === 'yaml'"></div>
+              </div>
+            </div>
+            <div class="method-text">
+              <div class="method-label">YAML Creation</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button class="cancel-button" @click="closeModal">
+          Cancel
+        </button>
+        <button 
+          class="continue-button" 
+          @click="continueToSelected"
+          :disabled="!selectedMethod"
+        >
+          Continue
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+
+export default defineComponent({
+  name: 'SecretsCreateMethodSelector',
+
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+
+  emits: ['close', 'method-selected'],
+
+  setup(props, { emit }) {
+    const selectedMethod = ref('guided');
+    const closeModal = () => {
+      emit('close');
+    };
+    const continueToSelected = () => {
+      emit('method-selected', selectedMethod.value);
+    };
+    return {
+      selectedMethod,
+      closeModal,
+      continueToSelected
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/CreateResource.css" scoped></style>

diff --git a/frontend/src/components/SecretsCreateYaml.vue b/frontend/src/components/SecretsCreateYaml.vue
line changes: +101/-0
index 0000000..743baea
--- /dev/null
+++ b/frontend/src/components/SecretsCreateYaml.vue
@@ -0,0 +1,101 @@
+<!-- SecretsCreateYaml.vue -->
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content yaml-editor" @click.stop>
+      <div class="modal-header">
+        <h3>Create Secret from YAML</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+
+        <div class="yaml-container">
+          <textarea 
+            v-model="yamlContent" 
+            placeholder="Paste your Secret YAML definition here..."
+            :disabled="isSubmitting"
+            class="yaml-textarea"
+          ></textarea>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="create-button" 
+          @click="createSecretFromYaml"
+          :disabled="isSubmitting || !yamlContent.trim()"
+        >
+          {{ isSubmitting ? 'Creating...' : 'Create Secret' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+
+export default defineComponent({
+  name: 'CreateSecretYaml',
+
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+
+  emits: ['close', 'create-secret'],
+
+  setup(props, { emit }) {
+    // State
+    const yamlContent = ref('');
+    const isSubmitting = ref(false);
+    const error = ref('');
+
+    // Methods
+    const closeModal = () => {
+      if (!isSubmitting.value) {
+        emit('close');
+      }
+    };
+
+    const createSecretFromYaml = async () => {
+      isSubmitting.value = true;
+      error.value = '';
+
+      try {
+        const opts = {
+          yamlContent: yamlContent.value,
+          isYaml: true
+        };
+
+        emit('create-secret', opts);
+        closeModal();
+      } catch (err: any) {
+        error.value = `Failed to create Secret: ${err.message || err}`;
+      } finally {
+        isSubmitting.value = false;
+      }
+    };
+
+    return {
+      yamlContent,
+      isSubmitting,
+      error,
+      closeModal,
+      createSecretFromYaml
+    };
+  }
+});
+</script>
+
+<style src="@/assets/css/MenuDialog.css" scoped></style>

diff --git a/frontend/src/components/SecretsEdit.vue b/frontend/src/components/SecretsEdit.vue
line changes: +153/-0
index 0000000..0a5e7a9
--- /dev/null
+++ b/frontend/src/components/SecretsEdit.vue
@@ -0,0 +1,153 @@
+<!-- SecretsEdit.vue -->
+<script lang="ts">
+import { defineComponent, ref, watch, PropType } from 'vue';
+import { KubernetesSecret, KubernetesCluster } from '../types/kubernetes';
+import { kubernetesService } from '../lib/kubernetes';
+import { secretJsonToYaml } from '../lib/lib';
+import { SecretUpdateOptions } from '../types/custom';
+
+export default defineComponent({
+  name: 'SecretsEdit',
+
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    secret: {
+      type: Object as PropType<KubernetesSecret | null>,
+      default: null
+    },
+    cluster: {
+      type: Object as PropType<KubernetesCluster | null>,
+      default: null
+    }
+  },
+
+  emits: ['close', 'update-secret'],
+  setup(props, { emit }) {
+    // State
+    const yamlContent = ref('');
+    const isLoading = ref(false);
+    const isSubmitting = ref(false);
+    const error = ref('');
+    const originalSecretName = ref('');
+
+    // Watch for changes to show prop
+    watch(() => props.show, (newVal) => {
+      if (newVal && props.secret) {
+        fetchSecretYaml();
+      } else {
+        yamlContent.value = '';
+        error.value = '';
+      }
+    });
+
+    // Methods
+    const closeModal = () => {
+      if (!isSubmitting.value) {
+        emit('close');
+      }
+    };
+
+    const fetchSecretYaml = async () => {
+      if (!props.secret || !props.cluster) return;
+
+      isLoading.value = true;
+      error.value = '';
+
+      originalSecretName.value = props.secret.metadata.name;
+
+      try {
+        const response = await kubernetesService.getSecret(
+          props.cluster.contextName,
+          props.secret.metadata.namespace,
+          props.secret.metadata.name
+        );
+
+        if (response.success) {
+          yamlContent.value = secretJsonToYaml(response.data);
+        } else {
+          error.value = response.msg || 'Failed to fetch secret YAML';
+        }
+      } catch (err: any) {
+        error.value = `Error fetching secret YAML: ${err.message || err}`;
+      } finally {
+        isLoading.value = false;
+      }
+    };
+
+    const updateSecret = (): void => {
+      const opts: SecretUpdateOptions = {
+        context: props.cluster.contextName,
+        definition: yamlContent.value,
+        origNamespace: props.secret.metadata.namespace,
+        origName: props.secret.metadata.name
+      }
+      try {
+        emit('update-secret', opts)
+        closeModal()
+      } catch (error: any) {
+        alert(error)
+      }
+      return
+    };
+
+    return {
+      yamlContent,
+      isLoading,
+      isSubmitting,
+      error,
+      originalSecretName,
+      closeModal,
+      fetchSecretYaml,
+      updateSecret
+    };
+  }
+});
+</script>
+
+
+<template>
+  <div v-if="show" class="modal-overlay" @click="closeModal">
+    <div class="modal-content yaml-editor" @click.stop>
+      <div class="modal-header">
+        <h3>Edit Secret YAML</h3>
+        <button class="close-button" @click="closeModal">×</button>
+      </div>
+
+      <div class="modal-body">
+        <div v-if="error" class="error-message">{{ error }}</div>
+        <div v-if="isLoading" class="loading-message">Loading secret YAML...</div>
+
+        <div v-if="!isLoading" class="yaml-container">
+          <textarea 
+            v-model="yamlContent" 
+            :disabled="isSubmitting"
+            class="yaml-textarea"
+          ></textarea>
+        </div>
+      </div>
+
+      <div class="modal-footer">
+        <button 
+          class="cancel-button" 
+          @click="closeModal"
+          :disabled="isSubmitting"
+        >
+          Cancel
+        </button>
+        <button 
+          class="update-button" 
+          @click="updateSecret"
+          :disabled="isSubmitting || isLoading || !yamlContent.trim()"
+        >
+          {{ isSubmitting ? 'Updating...' : 'Update Secret' }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+
+<style src="@/assets/css/MenuDialog.css" scoped></style>

diff --git a/frontend/src/components/SecretsList.vue b/frontend/src/components/SecretsList.vue
line changes: +357/-0
index 0000000..af653a5
--- /dev/null
+++ b/frontend/src/components/SecretsList.vue
@@ -0,0 +1,357 @@
+<!-- SecretsList.vue -->
+<script lang="ts">
+import { defineComponent, ref, computed, PropType, onBeforeUnmount } from 'vue';
+import { KubernetesCluster, KubernetesSecret } from '../types/kubernetes';
+import { formatAge } from '../lib/format'; 
+import SearchBar from './SearchBar.vue';
+import SecretsCreateMethodSelector from './SecretsCreateMethodSelector.vue';
+import SecretsCreateYaml from './SecretsCreateYaml.vue';
+import SecretsCreateGuided from './SecretsCreateGuided.vue';
+import SecretsEdit from './SecretsEdit.vue';
+import { SecretUpdateOptions } from '../types/custom';
+
+export default defineComponent({
+  name: 'SecretsList',
+
+  components: {
+    SearchBar,
+    SecretsCreateMethodSelector,
+    SecretsCreateYaml,
+    SecretsCreateGuided,
+    SecretsEdit
+  },
+
+  props: {
+    selectedCluster: {
+      type: Object as PropType<KubernetesCluster>,
+      required: false,
+      default: null
+    },
+    secrets: {
+      type: Array as PropType<KubernetesSecret[]>,
+      required: false,
+      default: () => []
+    }
+  },
+
+  emits: ['secret-selected', 'delete-secret', 'create-secret', 'update-secret'],
+
+  setup(props, { emit }) {
+    // State
+    const searchQuery = ref('');
+    const selectedSecret = ref<KubernetesSecret | null>(null);
+    const selectedContextSecret = ref<KubernetesSecret | null>(null);
+    const showMenu = ref(false);
+    const menuPosition = ref({ x: 0, y: 0 });
+
+    const showMethodSelector = ref(false);
+    const showCreateGuided = ref(false);
+    const showCreateYaml = ref(false);
+    const showEditYaml = ref(false);
+
+    // Computed properties
+    const filteredSecrets = computed(() => {
+      if (!searchQuery.value) {
+        return props.secrets;
+      }
+
+      const query = searchQuery.value.toLowerCase();
+
+      const nameSpecificMatch = query.match(/^name:(.+)$/);
+      if (nameSpecificMatch) {
+        const nameQuery = nameSpecificMatch[1].trim();
+        return props.secrets.filter(secret => {
+          const name = secret.metadata.name.toLowerCase();
+          return name.includes(nameQuery);
+        });
+      }
+
+      const statusSpecificMatch = query.match(/^status:(.+)$/);
+      if (statusSpecificMatch) {
+        const statusQuery = statusSpecificMatch[1].trim();
+        return props.secrets.filter(secret => {
+          const status = secret.status?.phase?.toLowerCase() || '';
+          return status.includes(statusQuery);
+        });
+      }
+
+      const labelSpecificMatch = query.match(/^label:(.+)$/);
+      if (labelSpecificMatch) {
+        const labelQuery = labelSpecificMatch[1].trim();
+        return props.secrets.filter(secret => {
+          const labels = secret.metadata.labels || {};
+          for (const key in labels) {
+            const value = labels[key].toLowerCase();
+            const keyLower = key.toLowerCase();
+
+            // Check for key=value format
+            if (labelQuery.includes('=')) {
+              const [queryKey, queryValue] = labelQuery.split('=');
+              if (keyLower === queryKey.trim().toLowerCase() && 
+                  value.includes(queryValue.trim().toLowerCase())) {
+                return true;
+              }
+            } 
+            // Check for just key
+            else if (keyLower.includes(labelQuery)) {
+              return true;
+            }
+          }
+          return false;
+        });
+      }
+
+      // Default behavior: search only in secret names
+      return props.secrets.filter(secret => {
+        const name = secret.metadata.name.toLowerCase();
+        return name.includes(query);
+      });
+    });
+
+    // Methods
+    const hideContextMenu = () => {
+      showMenu.value = false;
+      document.removeEventListener('click', hideContextMenu);
+    };
+
+    const showContextMenu = (event: MouseEvent, secret: KubernetesSecret) => {
+      event.preventDefault();
+
+      menuPosition.value = {
+        x: event.clientX,
+        y: event.clientY
+      };
+
+      selectedContextSecret.value = secret;
+      selectSecret(secret);
+      showMenu.value = true;
+
+      setTimeout(() => {
+        document.addEventListener('click', hideContextMenu);
+      }, 0);
+    };
+
+    const isSelected = (secret: KubernetesSecret): boolean => {
+      if (!selectedSecret.value) return false;
+      return selectedSecret.value.metadata.name === secret.metadata.name &&
+        selectedSecret.value.metadata.uid === secret.metadata.uid;
+    };
+
+    const selectSecret = (secret: KubernetesSecret): void => {
+      try {
+        const secretToEmit = { ...secret };
+        if (!secretToEmit.kind) {
+          secretToEmit.kind = 'Secret';
+        }
+        selectedSecret.value = secretToEmit;
+        emit('secret-selected', secretToEmit);
+      } catch (error) {
+        console.error("Failed to select secret:", error);
+      }
+    };
+
+    const deleteSecret = (): void => {
+      if (selectedContextSecret.value && props.selectedCluster) {
+        try {
+          emit('delete-secret', {
+            cluster: props.selectedCluster,
+            secret: selectedContextSecret.value
+          });
+        } catch (error) {
+          alert(error);
+        }
+      }
+      hideContextMenu();
+    };
+
+    const getUniqueKey = (secret: KubernetesSecret): string => {
+      return `${secret.metadata.name}-${secret.metadata.uid || ''}`;
+    };
+
+    const getSecretStatus = (secret: KubernetesSecret): string => {
+      return secret.status?.phase || 'Unknown';
+    };
+
+    const getSecretStatusClass = (secret: KubernetesSecret): string => {
+      const status = getSecretStatus(secret);
+      switch (status) {
+        case 'Active':
+          return 'status-active';
+        case 'Terminating':
+          return 'status-terminating';
+        default:
+          return 'status-unknown';
+      }
+    };
+
+    const editSecret = (): void => {
+      showEditYaml.value = true;
+      hideContextMenu();
+    };
+
+    const createSecret = (): void => {
+      showMethodSelector.value = true;
+      hideContextMenu();
+    };
+
+    const handleMethodSelected = (method: string): void => {
+      showMethodSelector.value = false;
+      if (method === 'guided') {
+        showCreateGuided.value = true;
+      } else if (method === 'yaml') {
+        showCreateYaml.value = true;
+      }
+    };
+
+    const handleUpdateSecret = (opts: SecretUpdateOptions): void => {
+      try {
+        emit('update-secret', opts)
+      } catch (error: any) {
+        alert(error)
+      }
+      return
+    }
+
+    const handleCreateSecret = (opts: SecretCreateOptions): void => {
+      try {
+        emit('create-secret', opts)
+      } catch (err: any) {
+        console.error('Failed to create Secret:', err)
+      } finally {
+        showCreateGuided.value = false
+        showCreateYaml.value = false
+      }
+    }
+
+    // Lifecycle hooks
+    onBeforeUnmount(() => {
+      document.removeEventListener('click', hideContextMenu);
+    });
+
+    // Return all refs, computed properties, and methods
+    return {
+      // State
+      showMethodSelector,
+      searchQuery,
+      selectedSecret,
+      selectedContextSecret,
+      showMenu,
+      showEditYaml,
+      menuPosition,
+      showCreateGuided,
+      showCreateYaml,
+
+      // Computed
+      filteredSecrets,
+
+      // Methods
+      hideContextMenu,
+      showContextMenu,
+      isSelected,
+      selectSecret,
+      deleteSecret,
+      getUniqueKey,
+      getSecretStatus,
+      getSecretStatusClass,
+      formatAge,
+      handleMethodSelected,
+      createSecret,
+      editSecret,
+      handleCreateSecret,
+      handleUpdateSecret
+    };
+  }
+});
+</script>
+
+<template>
+  <div class="secrets-container">
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search secrets..."
+      />
+    </div>
+
+    <div v-if="filteredSecrets.length === 0" class="no-secrets">
+      <p v-if="searchQuery">No secrets found matching "{{ searchQuery }}"</p>
+      <p v-else>No secrets found.</p>
+    </div>
+
+    <div v-else class="table-scroll-container"> 
+      <table>
+        <thead>
+          <tr>
+            <th>Namespace</th>
+            <th>Name</th>
+            <th>Type</th>
+            <th>Data</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="secret in filteredSecrets" 
+            :key="getUniqueKey(secret)"
+            :class="{ selected: isSelected(secret) }"
+            @click="selectSecret(secret)"
+            @contextmenu="showContextMenu($event, secret)"
+          >
+            <td>{{ secret.metadata.namespace }}</td>
+            <td>{{ secret.metadata.name }}</td>
+            <td>{{ secret.type }}</td>
+            <td>{{ secret.data ? Object.keys(secret.data).length : 0 }}</td>
+            <td>{{ formatAge(secret.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+      <div 
+        v-if="showMenu" 
+        class="context-menu" 
+        :style="{ top: menuPosition.y + 'px', left: menuPosition.x + 'px' }"
+        @click.stop
+      >
+        <div class="menu-item" @click="createSecret">
+          Create
+        </div>
+        <div class="menu-item" @click="editSecret">
+          Edit
+        </div>
+        <div class="menu-item" @click="deleteSecret">
+          Delete
+        </div>
+      </div>
+    </div>
+
+    <SecretsCreateMethodSelector
+        :show="showMethodSelector"
+        @close="showMethodSelector = false"
+        @method-selected="handleMethodSelected"
+        />
+
+    <SecretsCreateYaml
+        :show="showCreateYaml"
+        @close="showCreateYaml = false"
+        @create-secret="handleCreateSecret"
+        />
+
+    <SecretsCreateGuided
+        :show="showCreateGuided"
+        :cluster="selectedCluster"
+        @close="showCreateGuided = false"
+        @create-secret="handleCreateSecret"
+        />
+
+    <SecretsEdit
+        :show="showEditYaml"
+        :secret="selectedContextSecret"
+        :cluster="selectedCluster"
+        @close="showEditYaml = false"
+        @update-secret="handleUpdateSecret"
+        />
+
+  </div>
+</template>
+
+<style src="@/assets/css/ListResource.css" scoped></style>

diff --git a/frontend/src/components/StatefulsetsList.vue b/frontend/src/components/StatefulsetsList.vue
line changes: +224/-0
index 0000000..79f10f6
--- /dev/null
+++ b/frontend/src/components/StatefulsetsList.vue
@@ -0,0 +1,224 @@
+<script lang="ts">
+import { defineComponent, ref, computed, PropType, onBeforeUnmount } from 'vue';
+import { KubernetesCluster, KubernetesStatefulSet } from '../types/kubernetes';
+import SearchBar from './SearchBar.vue';
+import { formatAge } from '../lib/format';
+
+export default defineComponent({
+    name: 'StatefulSetsList',
+    components: {
+        SearchBar
+    },
+    props: {
+        selectedCluster: {
+            type: Object as PropType<KubernetesCluster>,
+            required: false,
+            default: null
+        },
+        statefulSets: {
+            type: Array as PropType<KubernetesStatefulSet[]>,
+            required: false,
+            default: () => []
+        }
+    },
+    emits: ['statefulset-selected', 'restart-statefulset'],
+    data() {
+        return {
+            selectedStatefulSet: null as KubernetesStatefulSet | null
+        };
+    },
+    setup(props, { emit }) {
+        const searchQuery = ref('');
+        const filteredStatefulSets = computed(() => {
+            if (!searchQuery.value) {
+                return props.statefulSets;
+            }
+
+            const query = searchQuery.value.toLowerCase();
+
+            const nameSpecificMatch = query.match(/^name:(.+)$/);
+            if (nameSpecificMatch) {
+                const nameQuery = nameSpecificMatch[1].trim();
+                return props.statefulSets.filter(statefulSet => {
+                    const name = statefulSet.metadata.name.toLowerCase();
+                    return name.includes(nameQuery);
+                });
+            }
+
+            const namespaceSpecificMatch = query.match(/^namespace:(.+)$/);
+            if (namespaceSpecificMatch) {
+                const namespaceQuery = namespaceSpecificMatch[1].trim();
+                return props.statefulSets.filter(statefulSet => {
+                    const namespace = statefulSet.metadata.namespace.toLowerCase();
+                    return namespace.includes(namespaceQuery);
+                });
+            }
+
+            const labelMatch = query.match(/^label:(.+)$/);
+            if (labelMatch) {
+                const labelQuery = labelMatch[1].trim();
+                return props.statefulSets.filter(statefulset => {
+                    const labels = statefulset.metadata.labels || {};
+                    for (const key in labels) {
+                        const value = labels[key].toLowerCase();
+                        const keyLower = key.toLowerCase();
+
+                        if (labelQuery.includes('=')) {
+                            const [queryKey, queryValue] = labelQuery.split('=');
+                            if (keyLower === queryKey.trim().toLowerCase() &&
+                                    value.includes(queryValue.trim().toLowerCase())) {
+                                return true;
+                            }
+                        } else if (keyLower.includes(labelQuery)) {
+                            return true;
+                        }
+                    }
+                    return false;
+                });
+            }
+
+            return props.statefulSets.filter(statefulSet => {
+                const name = statefulSet.metadata.name.toLowerCase();
+                return name.includes(query);
+            });
+        });
+
+        const showMenu = ref(false);
+        const menuPosition = ref({ x: 0, y: 0});
+        const selectedContextStatefulSet = ref<KubernetesStatefulSet | null>(null);
+
+        // Function to handle document click for hiding context menu
+        const handleDocumentClick = () => {
+            showMenu.value = false;
+        };
+
+        const showContextMenu = (event: MouseEvent, statefulSet: KubernetesStatefulSet) => {
+            event.preventDefault();
+            event.stopPropagation();
+
+            // Hide any existing menu first
+            showMenu.value = false;
+
+            // Set new menu position and selected item
+            menuPosition.value = {
+                x: event.clientX,
+                y: event.clientY
+            };
+
+            selectedContextStatefulSet.value = statefulSet;
+
+            // Show the menu after a small delay to avoid immediate closing
+            setTimeout(() => {
+                showMenu.value = true;
+                document.addEventListener('click', handleDocumentClick);
+            }, 10);
+        };
+
+        const hideContextMenu = () => {
+            showMenu.value = false;
+            document.removeEventListener('click', handleDocumentClick);
+        };
+
+        // Proper lifecycle cleanup
+        onBeforeUnmount(() => {
+            document.removeEventListener('click', handleDocumentClick);
+        });
+
+        return {
+            searchQuery,
+            filteredStatefulSets,
+            showMenu,
+            menuPosition,
+            selectedContextStatefulSet,
+            showContextMenu,
+            hideContextMenu,
+            formatAge
+        };
+    },
+    methods: {
+      isSelected(statefulSet: KubernetesStatefulSet): boolean {
+        return this.selectedStatefulSet?.metadata.name === statefulSet.metadata.name;
+      },
+      selectStatefulSet(statefulSet: KubernetesStatefulSet): void {
+        const statefulSetToEmit = { ...statefulSet };
+        if (!statefulSetToEmit.kind) {
+          statefulSetToEmit.kind = 'StatefulSet'
+        }
+        this.selectedStatefulSet = statefulSetToEmit;
+        this.$emit('statefulset-selected', statefulSetToEmit);
+      },
+      getUniqueKey(statefulSet: KubernetesStatefulSet): string {
+        let uniqueKey;
+        const namespace = statefulSet.metadata.namespace || 'default';
+        uniqueKey = namespace + '-' + statefulSet.metadata.name + '-' + (statefulSet.metadata.uid || '');
+        return uniqueKey;
+      },
+      restartStatefulSet() {
+        if (this.selectedContextStatefulSet && this.selectedCluster) {
+            this.$emit('restart-statefulset', {
+                cluster: this.selectedCluster,
+                statefulSet: this.selectedContextStatefulSet
+            });
+        }
+        this.hideContextMenu();
+      },
+      getReadyReplicas(statefulSet: KubernetesStatefulSet): string {
+        return (statefulSet.status.readyReplicas || 0) + '/' + statefulSet.spec.replicas;
+      }
+    },
+});
+</script>
+
+<template>
+  <div class="statefulsets-container">
+    <div class="search-bar-container">
+      <SearchBar
+        :value="searchQuery"
+        @update:value="searchQuery = $event"
+        placeholder="Search statefulsets..."
+      />
+    </div>
+    <div class="table-scroll-container">
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Namespace</th>
+            <th>Ready</th>
+            <th>Up-to-date</th>
+            <th>Available</th>
+            <th>Age</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr 
+            v-for="statefulSet in filteredStatefulSets" 
+            :key="getUniqueKey(statefulSet)"
+            :class="{ selected: isSelected(statefulSet) }"
+            @click="selectStatefulSet(statefulSet)"
+            @contextmenu.prevent="showContextMenu($event, statefulSet)"
+          >
+            <td>{{ statefulSet.metadata.name }}</td>
+            <td>{{ statefulSet.metadata.namespace || 'default' }}</td>
+            <td>{{ getReadyReplicas(statefulSet) }}</td>
+            <td>{{ statefulSet.status.updatedReplicas || 0 }}</td>
+            <td>{{ statefulSet.status.availableReplicas || 0 }}</td>
+            <td>{{ formatAge(statefulSet.metadata.creationTimestamp) }}</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <div 
+      v-if="showMenu" 
+      class="context-menu" 
+      :style="{ top: menuPosition.y + 'px', left: menuPosition.x + 'px' }"
+    >
+      <div class="menu-item" @click="restartStatefulSet">
+        <span class="menu-text">Restart</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style src="@/assets/css/StatefulSetsList.css" scoped></style>
+

diff --git a/frontend/src/components/Terminal.vue b/frontend/src/components/Terminal.vue
line changes: +301/-0
index 0000000..2f5281c
--- /dev/null
+++ b/frontend/src/components/Terminal.vue
@@ -0,0 +1,301 @@
+<script>
+import { Terminal } from '@xterm/xterm';
+import { FitAddon } from '@xterm/addon-fit';
+import { WebLinksAddon } from '@xterm/addon-web-links';
+import '@xterm/xterm/css/xterm.css';
+
+export default {
+  name: 'Terminal',
+
+  props: {
+    selectedPod: {
+      type: Object,
+      required: true
+    },
+    selectedNamespace: {
+      type: Object,
+      required: true
+    },
+    selectedCluster: {
+      type: Object,
+      required: true
+    }
+  },
+
+  data() {
+    return {
+      terminal: null,
+      fitAddon: null,
+      socket: null,
+      isConnected: false,
+      reconnectAttempts: 0,
+      maxReconnectAttempts: 3
+    }
+  },
+
+  mounted() {
+    this.initializeTerminal();
+    window.addEventListener('resize', this.onResize);
+  },
+
+  beforeUnmount() {
+    this.cleanup();
+    window.removeEventListener('resize', this.onResize);
+  },
+
+  methods: {
+    initializeTerminal() {
+      // Initialize xterm.js terminal
+      this.terminal = new Terminal({
+        cursorBlink: true,
+        fontSize: 14,
+        fontFamily: 'Menlo, Monaco, "Courier New", monospace',
+        theme: {
+          background: '#1e1e1e',
+          foreground: '#ffffff',
+          cursor: '#ffffff',
+          selection: 'rgba(255, 255, 255, 0.3)',
+          black: '#000000',
+          blue: '#2472c8',
+          brightBlue: '#3b8eea',
+          brightCyan: '#29b8db',
+          brightGreen: '#23d18b',
+          brightMagenta: '#d670d6',
+          brightRed: '#f14c4c',
+          brightWhite: '#e5e5e5',
+          brightYellow: '#f5f543',
+          cyan: '#11a8cd',
+          green: '#0dbc79',
+          magenta: '#bc3fbc',
+          red: '#cd3131',
+          white: '#e5e5e5',
+          yellow: '#e5e510'
+        }
+      });
+
+      // Add the fit addon
+      this.fitAddon = new FitAddon();
+      this.terminal.loadAddon(this.fitAddon);
+
+      // Add web links addon
+      this.terminal.loadAddon(new WebLinksAddon());
+
+      // Open terminal in the container
+      this.terminal.open(this.$refs.terminal);
+
+      // Initial fit
+      setTimeout(() => {
+        this.fitAddon.fit();
+      }, 100);
+
+      // Connect to WebSocket
+      this.connectWebSocket();
+    },
+
+    connectWebSocket() {
+      // If already connecting, don't try again
+      if (this.isReconnecting) {
+        return;
+      }
+
+      this.isReconnecting = true;
+
+      // Close existing socket if any
+      if (this.socket) {
+        this.socket.close();
+        this.socket = null;
+      }
+
+      const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
+      const wsUrl = `${wsProtocol}//${window.location.hostname}:8081/api/pods/exec?` + 
+        `cluster=${encodeURIComponent(this.selectedCluster.server)}&` +
+        `namespace=${encodeURIComponent(this.selectedNamespace.name)}&` +
+        `pod=${encodeURIComponent(this.selectedPod.metadata.name)}`;
+
+      try {
+        this.socket = new WebSocket(wsUrl);
+
+        this.socket.onopen = () => {
+          this.isConnected = true;
+          this.reconnectAttempts = 0;
+          this.isReconnecting = false;
+          this.terminal.writeln('Connected to pod shell...');
+          this.terminal.focus();
+        };
+
+        this.socket.onmessage = (event) => {
+          if (this.terminal) {
+            this.terminal.write(event.data);
+          }
+        };
+
+        this.socket.onclose = () => {
+          this.isConnected = false;
+          this.isReconnecting = false;
+          if (this.terminal) {
+            this.terminal.writeln('\r\nConnection closed');
+          }
+        };
+
+        this.socket.onerror = (error) => {
+          console.error('WebSocket error:', error);
+          this.isReconnecting = false;
+          if (this.terminal) {
+            this.terminal.writeln('\r\nError: Failed to connect to pod shell');
+          }
+        };
+
+        // Handle terminal input
+        if (!this.terminal._initialized) {
+          this.terminal.onData(data => {
+            if (this.isConnected && this.socket?.readyState === WebSocket.OPEN) {
+              this.socket.send(data);
+            }
+          });
+          this.terminal._initialized = true;
+        }
+
+      } catch (error) {
+        console.error('Failed to connect to WebSocket:', error);
+        this.isReconnecting = false;
+        if (this.terminal) {
+          this.terminal.writeln('Failed to connect to pod shell');
+        }
+      }
+    },
+
+    attemptReconnect() {
+      if (this.reconnectAttempts < this.maxReconnectAttempts) {
+        this.reconnectAttempts++;
+        this.terminal.writeln(`\r\nAttempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
+        setTimeout(() => {
+          this.connectWebSocket();
+        }, 2000);
+      }
+    },
+
+    onResize() {
+      if (this.fitAddon) {
+        this.fitAddon.fit();
+      }
+    },
+
+    
+    cleanup() {
+      if (this.socket) {
+        this.socket.close();
+        this.socket = null;
+      }
+
+      if (this.terminal) {
+        if (this.fitAddon) {
+          try {
+            this.fitAddon.dispose();
+          } catch (e) {
+            console.log('FitAddon cleanup:', e);
+          }
+          this.fitAddon = null;
+        }
+
+        try {
+          this.terminal.dispose();
+        } catch (e) {
+          console.log('Terminal cleanup:', e);
+        }
+        this.terminal = null;
+      }
+
+      this.isConnected = false;
+      this.isReconnecting = false;
+      this.reconnectAttempts = 0;
+    },
+
+    closeTerminal() {
+      this.cleanup();
+      this.$emit('close');
+    }
+
+  }
+}
+</script>
+
+<template>
+  <div class="terminal-container">
+    <div class="terminal-header">
+      <span>Terminal: {{ selectedPod?.metadata?.name }}</span>
+      <button @click="closeTerminal" class="close-btn">×</button>
+    </div>
+    <div id="terminal" ref="terminal" class="terminal-content"></div>
+  </div>
+</template>
+
+
+<style scoped>
+.terminal-container {
+  position: fixed;
+  bottom: 20px;
+  right: 20px;
+  width: 800px;
+  height: 500px;
+  background: #1e1e1e;
+  border: 1px solid #333;
+  border-radius: 6px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
+  z-index: 1000;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.terminal-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 12px;
+  background: #333;
+  color: white;
+  font-size: 14px;
+  user-select: none;
+}
+
+.close-btn {
+  background: none;
+  border: none;
+  color: white;
+  font-size: 20px;
+  cursor: pointer;
+  padding: 0 4px;
+  line-height: 1;
+}
+
+.close-btn:hover {
+  color: #ff5252;
+}
+
+.terminal-content {
+  flex: 1;
+  padding: 4px;
+  background-color: #1e1e1e;
+  text-align: left; /* Add this */
+}
+
+:deep(.xterm) {
+  padding: 8px;
+  height: 100%;
+  text-align: left; /* Add this */
+}
+
+:deep(.xterm-viewport) {
+  background-color: #1e1e1e !important;
+}
+
+/* Add these new styles */
+:deep(.xterm-screen) {
+  text-align: left !important;
+}
+
+:deep(.xterm-rows) {
+  text-align: left !important;
+}
+</style>
+

diff --git a/frontend/src/lib/format.ts b/frontend/src/lib/format.ts
line changes: +16/-0
index 0000000..394765f
--- /dev/null
+++ b/frontend/src/lib/format.ts
@@ -0,0 +1,16 @@
+/**
+ * Formats a timestamp into a human-readable age string (e.g., "3h" or "2d5h")
+ * 
+ * @param timestamp - ISO 8601 timestamp string
+ * @returns Formatted age string
+ */
+export function formatAge(timestamp: string): string {
+    const created = new Date(timestamp);
+    const now = new Date();
+    const diffHours = Math.floor((now.getTime() - created.getTime()) / (1000 * 60 * 60));
+    if (diffHours < 24) {
+      return `${diffHours}h`;
+    }
+    const diffDays = Math.floor(diffHours / 24);
+    return `${diffDays}d${diffHours % 24}h`;
+}

diff --git a/frontend/src/lib/kubernetes.ts b/frontend/src/lib/kubernetes.ts
line changes: +271/-0
index 0000000..73cf8c2
--- /dev/null
+++ b/frontend/src/lib/kubernetes.ts
@@ -0,0 +1,271 @@
+import { 
+	GetClusters,
+	GetDeployments,
+	GetPods,
+  GetPod,
+	GetStatefulSets,
+  GetNamespace,
+	RestartDeployment,
+  RestartStatefulSet,
+  DeletePod,
+  DescribePod,
+  GetConfigMaps,
+  DescribeConfigMap,
+  DescribeDeployment,
+  DescribeStatefulSet,
+  GetIngresses,
+  DescribeIngress,
+  DescribeNamespace,
+  DeleteIngress,
+  DeleteNamespace,
+  CreatePod,
+  CreatePodYAML,
+  CreateNamespace,
+  CreateNamespaceYAML,
+  UpdatePodFromYaml,
+  UpdateNamespaceFromYaml,
+  GetNamespaces
+} from '../../wailsjs/go/services/clusterService';
+import {
+  Get as GetSecret,
+  List as ListSecrets,
+  Describe as DescribeSecret,
+  Create as CreateSecret,
+  Delete as DeleteSecret,
+  Update as UpdateSecret
+} from '../../wailsjs/go/services/secretsService';
+import {
+  List as ListPersistentVolumeClaims,
+  Describe as DescribePersistentVolumeClaim,
+  Create as CreatePersistentVolumeClaim,
+  Delete as DeletePersistentVolumeClaim,
+} from '../../wailsjs/go/services/persistentVolumeClaimsService';
+import {
+  List as ListPersistentVolumes,
+  Describe as DescribePersistentVolume,
+  Create as CreatePersistentVolume,
+  Delete as DeletePersistentVolume,
+} from '../../wailsjs/go/services/persistentVolumesService';
+import { 
+  ApiResponse, 
+  KubernetesCluster,
+  KubernetesConfigMap,
+  KubernetesDeployment,
+  KubernetesIngress,
+  KubernetesNamespace
+} from '../types/kubernetes';
+
+export interface KubernetesService {
+    getClusters(): Promise<ApiResponse<KubernetesCluster[]>>;
+
+    getDeployments(contextName: string, namespace?: string): Promise<ApiResponse<any[]>>;
+    getStatefulSets(contextName: string, namespace?: string): Promise<ApiResponse<KubernetesDeployment[]>>;
+    getPods(clusterName: string, namespace?: string): Promise<ApiResponse<any[]>>;
+    getPod(context: string, namespace: string, name: string): Promise<ApiResponse<any>>;
+    getConfigMaps(contextName: string, namespace: string): Promise<ApiResponse<KubernetesConfigMap[]>>;
+    getIngresses(context: string, namespace: string): Promise<ApiResponse<KubernetesIngress[]>>;
+    getNamespaces(context: string): Promise<ApiResponse<KubernetesNamespace[]>>;
+    getNamespace(context: string, name: string): Promise<ApiResponse<any>>;
+    getSecret(context: string, namespace: string, name: string): Promise<ApiResponse<any>>;
+
+    listSecrets(context: string, namespace: string): Promise<ApiResponse<any>>;
+    listPersistentVolumeClaims(context: string, namespace: string): Promise<ApiResponse<any>>;
+    listPersistentVolumes(context: string, namespace: string): Promise<ApiResponse<any>>;
+
+    restartDeployment(clusterName: string, namespace: string, name: string): Promise<ApiResponse<any>>;
+    restartStatefulSet(clusterName: string, namespace: string, name: string): Promise<ApiResponse<any>>;
+
+    describePod(clusterName: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+    describeDeployment(context: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+    describeConfigMap(context: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+    describeStatefulSet(context: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+    describeIngress(context: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+    describeNamespace(context: string, namespace: string): Promise<ApiResponse<string>>;
+    describeSecret(context: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+    describePersistentVolumeClaim(context: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+    describePersistentVolume(context: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+
+    deletePod(clusterName: string, namespace: string, name: string): Promise<ApiResponse<any>>;
+    deleteIngress(context: string, namespace: string, name: string): Promise<ApiResponse<string>>;
+    deleteNamespace(context: string, name: string): Promise<ApiResponse<any>>;
+    deleteSecret(context: string, namespace: string, name: string): Promise<ApiResponse<any>>;
+    deletePersistentVolume(context: string, name: string): Promise<ApiResponse<any>>;
+    deletePersistentVolumeClaim(context: string, namespace: string, name: string): Promise<ApiResponse<any>>;
+
+    createPod(context: string, podDefinition: any): Promise<ApiResponse<any>>;
+    createPodYAML(context: string, s: string): Promise<ApiResponse<any>>;
+    createNamespace(context: string, definition: any): Promise<ApiResponse<KubernetesNamespace>>;
+    createNamespaceYAML(context: string, definition: any): Promise<ApiResponse<any>>;
+    createSecret(context: string, definition: any): Promise<ApiResponse<any>>;
+    createPersistentVolume(context: string, definition: any): Promise<ApiResponse<any>>;
+    createPersistentVolumeClaim(context: string, definition: any): Promise<ApiResponse<any>>;
+
+    updatePod(context: string, s: string, originalPodname: string, originalNamespace: string): Promise<ApiResponse<any>>;
+    updateNamespace(context: string, s: string, originalName: string): Promise<ApiResponse<any>>;
+    updateSecret(context: string, input: string, originalNamespace: string, originalName: string): Promise<ApiResponse<any>>;
+}
+
+class KubernetesServiceImpl implements KubernetesService {
+
+    async getClusters(): Promise<ApiResponse<KubernetesCluster[]>> {
+        return GetClusters();
+    }
+
+    async getDeployments(contextName: string, namespace?: string): Promise<ApiResponse<any[]>> {
+        return GetDeployments(contextName, namespace || '');
+    }
+
+    async getPods(clusterName: string, namespace?: string): Promise<ApiResponse<any[]>> {
+        return GetPods(clusterName, namespace || '');
+    }
+
+    async getPod(context: string, namespace: string, name: string): Promise<ApiResponse<any>> {
+      return GetPod(context, namespace, name);
+    }
+
+    async getIngresses(context: string, namespace?: string): Promise<ApiResponse<KubernetesIngress[]>> {
+        return GetIngresses(context, namespace || '');
+    }
+
+    async getNamespaces(context: string): Promise<ApiResponse<any>> {
+        return GetNamespaces(context);
+    }
+
+    async getNamespace(context: string, name: string): Promise<ApiResponse<any>> {
+        return GetNamespace(context, name);
+    }
+
+    async getSecret(context: string, namespace: string, name: string): Promise<ApiResponse<any>> {
+      return GetSecret(context, namespace, name);
+    }
+
+    async describePod(clusterName: string, namespace: string, name: string): Promise<ApiResponse<any>> {
+        return DescribePod(clusterName, namespace, name);
+    }
+
+    async describeDeployment(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+        return DescribeDeployment(context, namespace, name);
+    }
+
+    async describeConfigMap(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+        return DescribeConfigMap(context, namespace, name);
+    }
+
+    async describeStatefulSet(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+        return DescribeStatefulSet(context, namespace, name);
+    }
+
+    async describeIngress(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+        return DescribeIngress(context, namespace, name);
+    }
+
+    async describeNamespace(context: string, namespace: string): Promise<ApiResponse<string>> {
+        return DescribeNamespace(context, namespace);
+    }
+
+    async describeSecret(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+      return DescribeSecret(context, namespace, name);
+    }
+
+    async describePersistentVolumeClaim(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+      return DescribePersistentVolumeClaim(context, namespace, name);
+    }
+
+    async describePersistentVolume(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+      return DescribePersistentVolume(context, namespace, name);
+    }
+
+    async getStatefulSets(contextName: string, namespace?: string): Promise<ApiResponse<any[]>> {
+        return GetStatefulSets(contextName, namespace || '');
+    }
+
+    async restartDeployment(clusterName: string, namespace: string, name: string): Promise<ApiResponse<any>> {
+        return RestartDeployment(clusterName, namespace, name);
+    }
+
+    async restartStatefulSet(clusterName: string, namespace: string, name: string): Promise<ApiResponse<any>> {
+        return RestartStatefulSet(clusterName, namespace, name);
+    }
+
+    async getConfigMaps(contextName: string, namespace?: string): Promise<ApiResponse<KubernetesConfigMap[]>> {
+        return GetConfigMaps(contextName, namespace || '');
+    }
+
+    async deletePod(clusterName: string, namespace: string, name: string): Promise<ApiResponse<any>> {
+        return DeletePod(clusterName, namespace, name);
+    }
+
+    async deleteIngress(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+        return DeleteIngress(context, namespace, name);
+    }
+
+    async deleteNamespace(context: string, name: string): Promise<ApiResponse<string>> {
+        return DeleteNamespace(context, name);
+    }
+
+    async deleteSecret(context: string, namespace: string, name: string): Promise<ApiResponse<string>> {
+      return DeleteSecret(context, namespace, name);
+    }
+
+    async deletePersistentVolume(context: string, name: string): Promise<ApiResponse<any>> {
+      return DeletePersistentVolume(context, name);
+    }
+
+    async deletePersistentVolumeClaim(context: string, namespace: string, name: string): Promise<ApiResponse<any>> {
+      return DeletePersistentVolumeClaim(context, namespace, name);
+    }
+
+    async createPod(context: string, podDefinition: any): Promise<ApiResponse<string>> {
+        return CreatePod(context, podDefinition);
+    }
+
+    async createPodYAML(context: string, s: string): Promise<ApiResponse<string>> {
+        return CreatePodYAML(context, s);
+    }
+
+    async createNamespace(context: string, definition: any): Promise<ApiResponse<any>> {
+        return CreateNamespace(context, definition);
+    }
+
+    async createNamespaceYAML(context: string, s: string): Promise<ApiResponse<any>> {
+        return CreateNamespaceYAML(context, s);
+    }
+
+    async createSecret(context: string, input: any): Promise<ApiResponse<any>> {
+      return CreateSecret(context, input);
+    }
+
+    async createPersistentVolume(context: string, definition: any): Promise<ApiResponse<any>> {
+      return CreatePersistentVolume(context, definition);
+    }
+
+    async createPersistentVolumeClaim(context: string, definition: any): Promise<ApiResponse<any>> {
+      return CreatePersistentVolumeClaim(context, definition);
+    }
+
+    async updatePod(context: string, s: string, originalPodname: string, originalNamespace: string): Promise<ApiResponse<any>> {
+        return UpdatePodFromYaml(context, s, originalPodname, originalNamespace);
+    }
+
+  async updateNamespace(context: string, s: string, originalName: string): Promise<ApiResponse<any>> {
+    return UpdateNamespaceFromYaml(context, s, originalName);
+  }
+
+  async updateSecret(context: string, input: string, originalNamespace: string, originalName: string): Promise<ApiResponse<any>> {
+    return UpdateSecret(context, input, originalNamespace, originalName);
+  }
+
+  async listSecrets(context: string, namespace?: string): Promise<ApiResponse<any>> {
+    return ListSecrets(context, namespace || '');
+  }
+
+  async listPersistentVolumeClaims(context: string, namespace?: string): Promise<ApiResponse<any>> {
+    return ListPersistentVolumeClaims(context, namespace || '');
+  }
+
+  async listPersistentVolumes(context: string): Promise<ApiResponse<any>> {
+    return ListPersistentVolumes(context);
+  }
+}
+
+export const kubernetesService: KubernetesService = new KubernetesServiceImpl();

diff --git a/frontend/src/lib/lib.ts b/frontend/src/lib/lib.ts
line changes: +710/-0
index 0000000..8c61df9
--- /dev/null
+++ b/frontend/src/lib/lib.ts
@@ -0,0 +1,710 @@
+import jsYaml from 'js-yaml';
+
+/**
+ * Formats a Kubernetes resource into a human-readable description
+ * @param resource The Kubernetes resource to format
+ * @returns A formatted description string
+ */
+export function formatResourceDescription(resource: any): string {
+  if (!resource) {
+    return 'No resource data available';
+  }
+
+  let result = '';
+
+  // Extract metadata
+  const metadata = resource.metadata || {};
+  const spec = resource.spec || {};
+  const status = resource.status || {};
+
+  // Basic metadata
+  result += `Name:         ${metadata.name || '<unknown>'}\n`;
+  result += `Namespace:    ${metadata.namespace || '<unknown>'}\n`;
+  result += `Kind:         ${resource.kind || '<unknown>'}\n`;
+  result += `API Version:  ${resource.apiVersion || '<unknown>'}\n`;
+
+  // Creation timestamp
+  if (metadata.creationTimestamp) {
+    const creationDate = new Date(metadata.creationTimestamp);
+    result += `Created:      ${creationDate.toUTCString()}\n`;
+  }
+
+  // Labels
+  result += 'Labels:       ';
+  if (metadata.labels && Object.keys(metadata.labels).length > 0) {
+    result += '\n';
+    for (const [key, value] of Object.entries(metadata.labels)) {
+      result += `              ${key}=${value}\n`;
+    }
+  } else {
+    result += '<none>\n';
+  }
+
+  // Annotations
+  result += 'Annotations:  ';
+  if (metadata.annotations && Object.keys(metadata.annotations).length > 0) {
+    result += '\n';
+    for (const [key, value] of Object.entries(metadata.annotations)) {
+      result += `              ${key}=${value}\n`;
+    }
+  } else {
+    result += '<none>\n';
+  }
+
+  // Check if it's a Pod
+  const isPod = resource.kind === 'Pod';
+
+  // Check if it's a Deployment
+  const isDeployment = resource.kind === 'Deployment';
+
+  // Pod-specific information
+  if (isPod) {
+    // Pod status
+    const phase = status.phase || '<unknown>';
+    const podIP = status.podIP || '<none>';
+    const nodeName = spec.nodeName || '<none>';
+
+    result += `Status:       ${phase}\n`;
+    result += `IP:           ${podIP}\n`;
+    result += `Node:         ${nodeName}\n`;
+
+    // Containers
+    result += 'Containers:\n';
+    const containers = spec.containers || [];
+    if (containers.length > 0) {
+      for (const container of containers) {
+        result += `  ${container.name}:\n`;
+        result += `    Image:      ${container.image || '<none>'}\n`;
+
+        // Container ports
+        result += '    Ports:      ';
+        const ports = container.ports || [];
+        if (ports.length > 0) {
+          result += '\n';
+          for (const port of ports) {
+            const protocol = port.protocol || 'TCP';
+            const portStr = `      ${protocol}:${port.containerPort}`;
+            if (port.name) {
+              result += `${portStr} (${port.name})\n`;
+            } else {
+              result += `${portStr}\n`;
+            }
+          }
+        } else {
+          result += '<none>\n';
+        }
+
+        // Container environment variables
+        result += '    Environment:';
+        const env = container.env || [];
+        if (env.length > 0) {
+          result += '\n';
+          for (const envVar of env) {
+            let envStr = `      ${envVar.name}=`;
+            if (envVar.value !== undefined) {
+              envStr += envVar.value;
+            } else if (envVar.valueFrom) {
+              envStr += '<set from source>';
+            } else {
+              envStr += '<unset>';
+            }
+            result += `${envStr}\n`;
+          }
+        } else {
+          result += ' <none>\n';
+        }
+
+        // Container resources
+        result += '    Resources:  ';
+        const resources = container.resources || {};
+        if ((resources.limits && Object.keys(resources.limits).length > 0) || 
+            (resources.requests && Object.keys(resources.requests).length > 0)) {
+          result += '\n';
+
+          // Limits
+          if (resources.limits && Object.keys(resources.limits).length > 0) {
+            result += '      Limits:\n';
+            for (const [key, value] of Object.entries(resources.limits)) {
+              result += `        ${key}: ${value}\n`;
+            }
+          }
+
+          // Requests
+          if (resources.requests && Object.keys(resources.requests).length > 0) {
+            result += '      Requests:\n';
+            for (const [key, value] of Object.entries(resources.requests)) {
+              result += `        ${key}: ${value}\n`;
+            }
+          }
+        } else {
+          result += '<none>\n';
+        }
+
+        // Container status
+        const containerStatuses = status.containerStatuses || [];
+        for (const containerStatus of containerStatuses) {
+          if (containerStatus.name === container.name) {
+            result += `    Ready:       ${containerStatus.ready}\n`;
+            result += `    Restarts:    ${containerStatus.restartCount}\n`;
+
+            // Container state
+            result += '    State:       ';
+            const state = containerStatus.state || {};
+            if (state.running) {
+              const startedAt = state.running.startedAt;
+              if (startedAt) {
+                const startDate = new Date(startedAt);
+                result += `Running since ${startDate.toUTCString()}\n`;
+              } else {
+                result += 'Running\n';
+              }
+            } else if (state.waiting) {
+              const reason = state.waiting.reason || '';
+              const message = state.waiting.message || '';
+              result += `Waiting: ${reason} - ${message}\n`;
+            } else if (state.terminated) {
+              const reason = state.terminated.reason || '';
+              const exitCode = state.terminated.exitCode || 0;
+              result += `Terminated: ${reason} (exit code: ${exitCode})\n`;
+            } else {
+              result += 'Unknown\n';
+            }
+            break;
+          }
+        }
+      }
+    } else {
+      result += '  <none>\n';
+    }
+
+    // Pod conditions
+    result += 'Conditions:\n';
+    const conditions = status.conditions || [];
+    if (conditions.length > 0) {
+      for (const condition of conditions) {
+        let condStr = `  ${condition.type}: ${condition.status}`;
+        if (condition.reason) {
+          condStr += ` (${condition.reason})`;
+        }
+        if (condition.message) {
+          condStr += ` - ${condition.message}`;
+        }
+        result += `${condStr}\n`;
+      }
+    } else {
+      result += '  <none>\n';
+    }
+  }
+
+  // Deployment-specific information
+  if (isDeployment) {
+    // Replicas
+    const replicas = spec.replicas !== undefined ? spec.replicas : 0;
+    result += `Replicas:     ${replicas} desired`;
+
+    if (status) {
+      const updatedReplicas = status.updatedReplicas !== undefined ? status.updatedReplicas : 0;
+      const totalReplicas = status.replicas !== undefined ? status.replicas : 0;
+      const availableReplicas = status.availableReplicas !== undefined ? status.availableReplicas : 0;
+      const unavailableReplicas = status.unavailableReplicas !== undefined ? status.unavailableReplicas : 0;
+
+      result += ` | ${updatedReplicas} updated`;
+      result += ` | ${totalReplicas} total`;
+      result += ` | ${availableReplicas} available`;
+      result += ` | ${unavailableReplicas} unavailable`;
+    }
+    result += '\n';
+
+    // Strategy
+    let strategyType = 'Unknown';
+    if (spec.strategy && spec.strategy.type) {
+      strategyType = spec.strategy.type;
+    }
+    result += `Strategy:     ${strategyType}\n`;
+
+    // Selector
+    result += 'Selector:     ';
+    if (spec.selector && spec.selector.matchLabels && Object.keys(spec.selector.matchLabels).length > 0) {
+      result += '\n';
+      for (const [key, value] of Object.entries(spec.selector.matchLabels)) {
+        result += `              ${key}=${value}\n`;
+      }
+    } else {
+      result += '<none>\n';
+    }
+
+    // Pod template
+    if (spec.template) {
+      result += 'Pod Template:\n';
+
+      // Template labels
+      result += '  Labels:      ';
+      if (spec.template.metadata && spec.template.metadata.labels && 
+          Object.keys(spec.template.metadata.labels).length > 0) {
+        result += '\n';
+        for (const [key, value] of Object.entries(spec.template.metadata.labels)) {
+          result += `              ${key}=${value}\n`;
+        }
+      } else {
+        result += '<none>\n';
+      }
+
+      // Template containers
+      result += '  Containers:\n';
+      if (spec.template.spec && spec.template.spec.containers && spec.template.spec.containers.length > 0) {
+        for (const container of spec.template.spec.containers) {
+          result += `    ${container.name}:\n`;
+          result += `      Image:      ${container.image || '<none>'}\n`;
+
+          // Container ports
+          result += '      Ports:      ';
+          const ports = container.ports || [];
+          if (ports.length > 0) {
+            result += '\n';
+            for (const port of ports) {
+              const protocol = port.protocol || 'TCP';
+              const portStr = `        ${protocol}:${port.containerPort}`;
+              if (port.name) {
+                result += `${portStr} (${port.name})\n`;
+              } else {
+                result += `${portStr}\n`;
+              }
+            }
+          } else {
+            result += '<none>\n';
+          }
+
+          // Container environment variables
+          result += '      Environment:';
+          const env = container.env || [];
+          if (env.length > 0) {
+            result += '\n';
+            for (const envVar of env) {
+              let envStr = `        ${envVar.name}=`;
+              if (envVar.value !== undefined) {
+                envStr += envVar.value;
+              } else if (envVar.valueFrom) {
+                envStr += '<set from source>';
+              } else {
+                envStr += '<unset>';
+              }
+              result += `${envStr}\n`;
+            }
+          } else {
+            result += ' <none>\n';
+          }
+
+          // Container resources
+          result += '      Resources:  ';
+          const resources = container.resources || {};
+          if ((resources.limits && Object.keys(resources.limits).length > 0) || 
+              (resources.requests && Object.keys(resources.requests).length > 0)) {
+            result += '\n';
+
+            // Limits
+            if (resources.limits && Object.keys(resources.limits).length > 0) {
+              result += '        Limits:\n';
+              for (const [key, value] of Object.entries(resources.limits)) {
+                result += `          ${key}: ${value}\n`;
+              }
+            }
+
+            // Requests
+            if (resources.requests && Object.keys(resources.requests).length > 0) {
+              result += '        Requests:\n';
+              for (const [key, value] of Object.entries(resources.requests)) {
+                result += `          ${key}: ${value}\n`;
+              }
+            }
+          } else {
+            result += '<none>\n';
+          }
+        }
+      } else {
+        result += '    <none>\n';
+      }
+    }
+
+    // Deployment conditions
+    result += 'Conditions:\n';
+    const conditions = status.conditions || [];
+    if (conditions.length > 0) {
+      for (const condition of conditions) {
+        let condStr = `  ${condition.type}: ${condition.status}`;
+        if (condition.reason) {
+          condStr += ` (${condition.reason})`;
+        }
+        if (condition.message) {
+          condStr += ` - ${condition.message}`;
+        }
+        result += `${condStr}\n`;
+      }
+    } else {
+      result += '  <none>\n';
+    }
+  }
+
+  return result;
+}
+
+/**
+ * Determines if a resource is a Pod
+ * @param resource The Kubernetes resource to check
+ * @returns True if the resource is a Pod
+ */
+export function isPod(resource: any): boolean {
+  if (!resource) return false;
+
+  // Check if it's explicitly a Pod
+  if (resource.kind === 'Pod') return true;
+
+  // Check if it has containers in spec (Pod-like)
+  return !!(resource.spec && Array.isArray(resource.spec.containers));
+}
+
+/**
+ * Determines if a resource is a Deployment
+ * @param resource The Kubernetes resource to check
+ * @returns True if the resource is a Deployment
+ */
+export function isDeployment(resource: any): boolean {
+  if (!resource) return false;
+
+  // Check if it's explicitly a Deployment
+  if (resource.kind === 'Deployment') return true;
+
+  // Check if it has replicas in spec (Deployment-like)
+  return !!(resource.spec && typeof resource.spec.replicas !== 'undefined');
+}
+
+/**
+ * Extracts container names from a Kubernetes resource
+ * @param resource The Kubernetes resource object
+ * @returns Array of container names
+ */
+export function extractContainers(resource: any): string[] {
+  if (!resource) {
+    return [];
+  }
+
+  try {
+    // For Pod resources
+    if (isPod(resource)) {
+      const containers: string[] = [];
+
+      // Get containers
+      if (resource.spec?.containers && Array.isArray(resource.spec.containers)) {
+        containers.push(...resource.spec.containers.map((c: any) => c.name));
+      }
+
+      // Get init containers if they exist
+      if (resource.spec?.initContainers && Array.isArray(resource.spec.initContainers)) {
+        containers.push(...resource.spec.initContainers.map((c: any) => c.name));
+      }
+
+      // Get ephemeral containers if they exist
+      if (resource.spec?.ephemeralContainers && Array.isArray(resource.spec.ephemeralContainers)) {
+        containers.push(...resource.spec.ephemeralContainers.map((c: any) => c.name));
+      }
+
+      return containers;
+    }
+
+    // For Deployment resources - extract containers from template
+    if (isDeployment(resource)) {
+      const containers: string[] = [];
+
+      // Get containers from pod template
+      if (resource.spec?.template?.spec?.containers && 
+          Array.isArray(resource.spec.template.spec.containers)) {
+        containers.push(...resource.spec.template.spec.containers.map((c: any) => c.name));
+      }
+
+      // Get init containers from pod template if they exist
+      if (resource.spec?.template?.spec?.initContainers && 
+          Array.isArray(resource.spec.template.spec.initContainers)) {
+        containers.push(...resource.spec.template.spec.initContainers.map((c: any) => c.name));
+      }
+
+      return containers;
+    }
+
+    // For other resource types that might have containers
+    if (resource.spec?.containers && Array.isArray(resource.spec.containers)) {
+      return resource.spec.containers.map((c: any) => c.name);
+    }
+
+    // For StatefulSet, DaemonSet, Job, CronJob - similar structure to Deployment
+    if (resource.spec?.template?.spec?.containers && 
+        Array.isArray(resource.spec.template.spec.containers)) {
+      return resource.spec.template.spec.containers.map((c: any) => c.name);
+    }
+  } catch (error) {
+    console.error('Error extracting containers:', error);
+  }
+
+  return [];
+}
+
+export function podJsonToYaml(podJson: any): string {
+  try {
+    // Create a clean copy of the pod object
+    const cleanPod = cleanPodForEditing(podJson);
+
+    // Convert to YAML
+    const yamlString = jsYaml.dump(cleanPod, {
+      indent: 2,
+      lineWidth: -1, // Don't wrap lines
+      noRefs: true,  // Don't output YAML references
+      sortKeys: false // Preserve key order
+    });
+
+    return yamlString;
+  } catch (error) {
+    console.error('Error converting Pod JSON to YAML:', error);
+    return '# Error converting Pod to YAML format';
+  }
+}
+
+/**
+ * Cleans a Pod object for editing by removing runtime fields
+ * that shouldn't be edited by users
+ * 
+ * @param pod - The Pod object to clean
+ * @returns A cleaned Pod object suitable for editing
+ */
+function cleanPodForEditing(pod: any): any {
+  // Create a deep copy to avoid modifying the original
+  const cleanPod = JSON.parse(JSON.stringify(pod));
+
+  // Ensure apiVersion and kind are set
+  cleanPod.apiVersion = 'v1';
+  cleanPod.kind = 'Pod';
+
+  // Remove status field completely
+  delete cleanPod.status;
+
+  // Remove runtime fields from metadata
+  if (cleanPod.metadata) {
+    delete cleanPod.metadata.resourceVersion;
+    delete cleanPod.metadata.uid;
+    delete cleanPod.metadata.generation;
+    delete cleanPod.metadata.creationTimestamp;
+    delete cleanPod.metadata.deletionTimestamp;
+    delete cleanPod.metadata.deletionGracePeriodSeconds;
+    delete cleanPod.metadata.managedFields;
+    delete cleanPod.metadata.selfLink;
+    delete cleanPod.metadata.ownerReferences;
+
+    // Remove cluster-specific annotations
+    if (cleanPod.metadata.annotations) {
+      Object.keys(cleanPod.metadata.annotations).forEach(key => {
+        if (key.startsWith('kubernetes.io/') || 
+            key.startsWith('kubectl.kubernetes.io/')) {
+          delete cleanPod.metadata.annotations[key];
+        }
+      });
+
+      // If annotations is now empty, remove it
+      if (Object.keys(cleanPod.metadata.annotations).length === 0) {
+        delete cleanPod.metadata.annotations;
+      }
+    }
+  }
+
+  return cleanPod;
+}
+
+import * as jsYaml from 'js-yaml';
+
+/**
+ * Converts a Kubernetes Namespace JSON object to YAML format
+ * 
+ * @param namespaceJson - The Namespace object in JSON format
+ * @returns A YAML string representation of the Namespace
+ */
+export function namespaceJsonToYaml(namespaceJson: any): string {
+  try {
+    // Create a clean copy of the namespace object
+    const cleanNamespace = cleanNamespaceForEditing(namespaceJson);
+
+    // Convert to YAML
+    const yamlString = jsYaml.dump(cleanNamespace, {
+      indent: 2,
+      lineWidth: -1, // Don't wrap lines
+      noRefs: true,  // Don't output YAML references
+      sortKeys: false // Preserve key order
+    });
+
+    return yamlString;
+  } catch (error) {
+    console.error('Error converting Namespace JSON to YAML:', error);
+    return '# Error converting Namespace to YAML format';
+  }
+}
+
+/**
+ * Cleans a Namespace object for editing by removing runtime fields
+ * that shouldn't be edited by users
+ * 
+ * @param namespace - The Namespace object to clean
+ * @returns A cleaned Namespace object suitable for editing
+ */
+function cleanNamespaceForEditing(namespace: any): any {
+  // Create a deep copy to avoid modifying the original
+  const cleanNamespace = JSON.parse(JSON.stringify(namespace));
+
+  // Ensure apiVersion and kind are set
+  cleanNamespace.apiVersion = 'v1';
+  cleanNamespace.kind = 'Namespace';
+
+  // Remove status field completely
+  delete cleanNamespace.status;
+
+  // Remove runtime fields from metadata
+  if (cleanNamespace.metadata) {
+    delete cleanNamespace.metadata.resourceVersion;
+    delete cleanNamespace.metadata.uid;
+    delete cleanNamespace.metadata.generation;
+    delete cleanNamespace.metadata.creationTimestamp;
+    delete cleanNamespace.metadata.deletionTimestamp;
+    delete cleanNamespace.metadata.deletionGracePeriodSeconds;
+    delete cleanNamespace.metadata.managedFields;
+    delete cleanNamespace.metadata.selfLink;
+    delete cleanNamespace.metadata.ownerReferences;
+
+    // Remove cluster-specific annotations
+    if (cleanNamespace.metadata.annotations) {
+      Object.keys(cleanNamespace.metadata.annotations).forEach(key => {
+        if (key.startsWith('kubernetes.io/') || 
+            key.startsWith('kubectl.kubernetes.io/')) {
+          delete cleanNamespace.metadata.annotations[key];
+        }
+      });
+
+      // If annotations is now empty, remove it
+      if (Object.keys(cleanNamespace.metadata.annotations).length === 0) {
+        delete cleanNamespace.metadata.annotations;
+      }
+    }
+  }
+
+  // Remove finalizers if present (can cause issues when editing)
+  if (cleanNamespace.spec && cleanNamespace.spec.finalizers) {
+    delete cleanNamespace.spec.finalizers;
+  }
+
+  return cleanNamespace;
+}
+
+
+/**
+ * Converts a Kubernetes Secret JSON object to YAML format
+ * 
+ * @param secretJson - The Secret object in JSON format
+ * @returns A YAML string representation of the Secret
+ */
+export function secretJsonToYaml(secretJson: any): string {
+  try {
+    // Create a clean copy of the secret object
+    const cleanSecret = cleanSecretForEditing(secretJson);
+
+    // Convert to YAML
+    const yamlString = jsYaml.dump(cleanSecret, {
+      indent: 2,
+      lineWidth: -1, // Don't wrap lines
+      noRefs: true,  // Don't output YAML references
+      sortKeys: false // Preserve key order
+    });
+
+    return yamlString;
+  } catch (error) {
+    console.error('Error converting Secret JSON to YAML:', error);
+    return '# Error converting Secret to YAML format';
+  }
+}
+
+/**
+ * Cleans a Secret object for editing by removing runtime fields
+ * that shouldn't be edited by users
+ * 
+ * @param secret - The Secret object to clean
+ * @returns A cleaned Secret object suitable for editing
+ */
+function cleanSecretForEditing(secret: any): any {
+  // Create a deep copy to avoid modifying the original
+  const cleanSecret = JSON.parse(JSON.stringify(secret));
+
+  // Ensure apiVersion and kind are set
+  cleanSecret.apiVersion = 'v1';
+  cleanSecret.kind = 'Secret';
+
+  // Remove status field completely
+  delete cleanSecret.status;
+
+  // Remove runtime fields from metadata
+  if (cleanSecret.metadata) {
+    delete cleanSecret.metadata.resourceVersion;
+    delete cleanSecret.metadata.uid;
+    delete cleanSecret.metadata.generation;
+    delete cleanSecret.metadata.creationTimestamp;
+    delete cleanSecret.metadata.deletionTimestamp;
+    delete cleanSecret.metadata.deletionGracePeriodSeconds;
+    delete cleanSecret.metadata.managedFields;
+    delete cleanSecret.metadata.selfLink;
+    delete cleanSecret.metadata.ownerReferences;
+
+    // Remove cluster-specific annotations
+    if (cleanSecret.metadata.annotations) {
+      Object.keys(cleanSecret.metadata.annotations).forEach(key => {
+        if (key.startsWith('kubernetes.io/') || 
+            key.startsWith('kubectl.kubernetes.io/')) {
+          delete cleanSecret.metadata.annotations[key];
+        }
+      });
+
+      // If annotations is now empty, remove it
+      if (Object.keys(cleanSecret.metadata.annotations).length === 0) {
+        delete cleanSecret.metadata.annotations;
+      }
+    }
+  }
+
+  // Set default type if not present
+  if (!cleanSecret.type) {
+    cleanSecret.type = 'Opaque';
+  }
+
+  // Convert data to stringData for easier editing
+  // This allows users to edit the values as plain text instead of base64
+  if (cleanSecret.data && Object.keys(cleanSecret.data).length > 0) {
+    cleanSecret.stringData = {};
+
+    // Decode base64 data to stringData
+    Object.keys(cleanSecret.data).forEach(key => {
+      try {
+        const decodedValue = atob(cleanSecret.data[key]);
+        cleanSecret.stringData[key] = decodedValue;
+      } catch (error) {
+        // If decoding fails, keep the original base64 value in data
+        console.warn(`Failed to decode base64 value for key "${key}":`, error);
+      }
+    });
+
+    // Remove the original data field since we're using stringData
+    delete cleanSecret.data;
+  }
+
+  // If there's existing stringData, preserve it
+  if (cleanSecret.stringData && Object.keys(cleanSecret.stringData).length === 0) {
+    delete cleanSecret.stringData;
+  }
+
+  // Ensure we have either data or stringData
+  if (!cleanSecret.data && !cleanSecret.stringData) {
+    cleanSecret.stringData = {};
+  }
+
+  return cleanSecret;
+}
+

diff --git a/frontend/src/lib/logs.ts b/frontend/src/lib/logs.ts
line changes: +308/-0
index 0000000..d19c3f5
--- /dev/null
+++ b/frontend/src/lib/logs.ts
@@ -0,0 +1,308 @@
+// lib/logs.ts
+import { KubernetesCluster } from '../types/kubernetes';
+import { isPod, isDeployment } from './resources';
+
+interface LogsOptions {
+  selectedResource: any;
+  selectedCluster: KubernetesCluster;
+  selectedContainer: string;
+  tailLines: number;
+  stripColors?: boolean;
+}
+
+interface LogsConnection {
+  ws: WebSocket | null;
+  connecting: boolean;
+  closeTimeout: number | null;
+  error: string;
+  logs: string;
+  onMessage?: (logs: string) => void;
+  onError?: (error: string) => void;
+  onOpen?: () => void;
+  onClose?: (code: number, reason: string) => void;
+}
+
+export class LogsService {
+  private connection: LogsConnection = {
+    ws: null,
+    connecting: false,
+    closeTimeout: null,
+    error: '',
+    logs: ''
+  };
+
+  /**
+   * Connect to logs WebSocket
+   */
+  connectLogsWebSocket(options: LogsOptions, callbacks?: {
+    onMessage?: (logs: string) => void;
+    onError?: (error: string) => void;
+    onOpen?: () => void;
+    onClose?: (code: number, reason: string) => void;
+  }): void {
+    const { selectedResource, selectedCluster, selectedContainer, tailLines, stripColors = true } = options;
+
+    // Store callbacks
+    if (callbacks) {
+      this.connection.onMessage = callbacks.onMessage;
+      this.connection.onError = callbacks.onError;
+      this.connection.onOpen = callbacks.onOpen;
+      this.connection.onClose = callbacks.onClose;
+    }
+
+    // If we're already connecting, don't try to connect again
+    if (this.connection.connecting) {
+      console.log('Already connecting to logs WebSocket, ignoring duplicate request');
+      return;
+    }
+
+    if (!selectedResource || !selectedCluster) {
+      this.connection.logs = 'No resource selected';
+      return;
+    }
+
+    this.connection.connecting = true;
+    this.connection.logs = 'Connecting to logs...';
+    this.connection.error = '';
+
+    try {
+      // Determine the WebSocket URL based on resource type
+      let wsUrl = '';
+
+      if (isPod(selectedResource)) {
+        wsUrl = `ws://127.0.0.1:8081/api/pods/logs?` + new URLSearchParams({
+          contextName: selectedCluster.contextName,
+          namespace: selectedResource.metadata.namespace,
+          name: selectedResource.metadata.name,
+          container: selectedContainer || '',
+          tailLines: tailLines.toString(),
+          stripColors: stripColors ? 'true' : 'false'
+        }).toString();
+      } else if (isDeployment(selectedResource)) {
+        wsUrl = `ws://127.0.0.1:8081/api/deployments/logs?` + new URLSearchParams({
+          contextName: selectedCluster.contextName,
+          namespace: selectedResource.metadata.namespace,
+          name: selectedResource.metadata.name,
+          tailLines: tailLines.toString(),
+          stripColors: stripColors ? 'true' : 'false'
+        }).toString();
+      } else {
+        this.connection.logs = 'Logs not available for this resource type';
+        this.connection.connecting = false;
+        return;
+      }
+
+      console.log(`Connecting to logs WebSocket: ${wsUrl}`);
+
+      // Create WebSocket connection
+      this.connection.ws = new WebSocket(wsUrl);
+
+      // Set up event handlers
+      this.connection.ws.onopen = () => {
+        console.log('Logs WebSocket connection established');
+        this.connection.logs = '';
+        this.connection.connecting = false;
+        this.connection.error = '';
+
+        if (this.connection.onOpen) {
+          this.connection.onOpen();
+        }
+      };
+
+      this.connection.ws.onmessage = (event) => {
+        // Append new log lines to the existing logs
+        this.connection.logs += event.data;
+
+        if (this.connection.onMessage) {
+          this.connection.onMessage(event.data);
+        }
+      };
+
+      this.connection.ws.onerror = (error) => {
+        console.error('WebSocket error:', error);
+        this.connection.error = 'Error connecting to logs';
+        this.connection.connecting = false;
+
+        if (this.connection.onError) {
+          this.connection.onError(this.connection.error);
+        }
+      };
+
+      this.connection.ws.onclose = (event) => {
+        console.log(`Logs WebSocket connection closed: ${event.code} ${event.reason}`);
+        this.connection.connecting = false;
+
+        // Only set error if it wasn't a normal closure and we don't already have an error
+        if (event.code !== 1000 && event.code !== 1001 && !this.connection.error) {
+          // Don't show error for code 1006 (abnormal closure) as it's common when navigating away
+          if (event.code !== 1006) {
+            this.connection.error = `Connection closed: ${event.reason || 'Server disconnected'}`;
+
+            if (this.connection.onError) {
+              this.connection.onError(this.connection.error);
+            }
+          }
+        }
+
+        // Clear the WebSocket reference
+        this.connection.ws = null;
+
+        // Clear any pending close timeout
+        if (this.connection.closeTimeout !== null) {
+          clearTimeout(this.connection.closeTimeout);
+          this.connection.closeTimeout = null;
+        }
+
+        if (this.connection.onClose) {
+          this.connection.onClose(event.code, event.reason);
+        }
+      };
+    } catch (error) {
+      console.error('Error setting up WebSocket:', error);
+      this.connection.error = `Error setting up WebSocket: ${error}`;
+      this.connection.logs = 'Failed to connect to logs';
+      this.connection.connecting = false;
+
+      if (this.connection.onError) {
+        this.connection.onError(this.connection.error);
+      }
+    }
+  }
+
+  /**
+   * Close logs WebSocket connection
+   */
+  closeLogsWebSocket(): Promise<void> {
+    return new Promise<void>((resolve) => {
+      // Clear any existing timeout
+      if (this.connection.closeTimeout !== null) {
+        clearTimeout(this.connection.closeTimeout);
+        this.connection.closeTimeout = null;
+      }
+
+      if (this.connection.ws) {
+        console.log('Closing logs WebSocket connection');
+
+        // Set a flag to track if onclose was called
+        let onCloseCalled = false;
+
+        // Create a temporary onclose handler to know when the connection is fully closed
+        const originalOnClose = this.connection.ws.onclose;
+        this.connection.ws.onclose = (event) => {
+          // Mark that onclose was called
+          onCloseCalled = true;
+
+          // Call the original handler if it exists
+          if (originalOnClose) {
+            originalOnClose.call(this.connection.ws, event);
+          }
+
+          resolve();
+        };
+
+        // Try to close the connection gracefully
+        try {
+          this.connection.ws.close(1000, 'User navigated away');
+        } catch (error) {
+          console.error('Error closing WebSocket:', error);
+        }
+
+        // Set a timeout in case the onclose event doesn't fire
+        this.connection.closeTimeout = window.setTimeout(() => {
+          if (!onCloseCalled) {
+            console.log('WebSocket close timed out, forcing cleanup');
+
+            // If we have an original onclose handler, call it with a simulated event
+            if (originalOnClose && this.connection.ws) {
+              try {
+                originalOnClose.call(this.connection.ws, {
+                  type: 'close',
+                  code: 1006,
+                  reason: 'Timeout while closing',
+                  wasClean: false
+                } as CloseEvent);
+              } catch (error) {
+                console.error('Error calling original onclose handler:', error);
+              }
+            }
+
+            // Clear the WebSocket reference
+            this.connection.ws = null;
+            this.connection.connecting = false;
+          }
+
+          this.connection.closeTimeout = null;
+          resolve();
+        }, 300); // Shorter timeout to reduce waiting time
+      } else {
+        // No WebSocket to close
+        this.connection.connecting = false;
+        resolve();
+      }
+    });
+  }
+
+  /**
+   * Reconnect logs WebSocket
+   */
+  async reconnectLogsWebSocket(options: LogsOptions, callbacks?: {
+    onMessage?: (logs: string) => void;
+    onError?: (error: string) => void;
+    onOpen?: () => void;
+    onClose?: (code: number, reason: string) => void;
+  }): Promise<void> {
+    try {
+      // Ensure we close any existing connection first
+      await this.closeLogsWebSocket();
+
+      // Wait a short time to ensure the previous connection is fully closed
+      await new Promise(resolve => setTimeout(resolve, 100));
+
+      // Now connect the new WebSocket
+      this.connectLogsWebSocket(options, callbacks);
+    } catch (error) {
+      console.error('Error reconnecting WebSocket:', error);
+      this.connection.error = 'Error reconnecting to logs';
+
+      if (callbacks?.onError) {
+        callbacks.onError(this.connection.error);
+      }
+    }
+  }
+
+  /**
+   * Get current logs
+   */
+  getLogs(): string {
+    return this.connection.logs;
+  }
+
+  /**
+   * Clear logs
+   */
+  clearLogs(): void {
+    this.connection.logs = '';
+  }
+
+  /**
+   * Get current error
+   */
+  getError(): string {
+    return this.connection.error;
+  }
+
+
+  /**
+   * Extract container names from resource
+   */
+  extractContainers(resource: any): string[] {
+    if (!resource || !resource.spec || !resource.spec.containers) {
+      return [];
+    }
+
+    return resource.spec.containers.map((c: any) => c.name);
+  }
+}
+
+// Create a singleton instance
+export const logsService = new LogsService();

diff --git a/frontend/src/lib/resources.ts b/frontend/src/lib/resources.ts
line changes: +64/-0
index 0000000..118bd1b
--- /dev/null
+++ b/frontend/src/lib/resources.ts
@@ -0,0 +1,64 @@
+export type KubernetesResourceKind =
+  'Pod' |
+  'Deployment' |
+  'StatefulSet' |
+  'ConfigMap' |
+  'Ingress' |
+  'Namespace' |
+  'Secret' |
+  'PersistentVolumeClaim' |
+  'PersistentVolume' |
+  'Unknown';
+
+export function getResourceKind(resource: any): KubernetesResourceKind {
+  if (resource.kind) {
+    switch (resource.kind) {
+      case 'Pod':
+        return 'Pod';
+      case 'Deployment':
+        return 'Deployment';
+      case 'StatefulSet':
+        return 'StatefulSet';
+      case 'ConfigMap':
+        return 'ConfigMap';
+      case 'Ingress':
+        return 'Ingress';
+      case 'Namespace':
+        return 'Namespace';
+      case 'Secret':
+        return 'Secret';
+      case 'PersistentVolumeClaim':
+        return 'PersistentVolumeClaim';
+      case 'PersistentVolume':
+        return 'PersistentVolume';
+      default:
+        return 'Unknown'
+    }
+  }
+  return 'Unknown';
+}
+
+/**
+* Check if resource is a pod
+*/
+export function isPod(resource: any): boolean {
+  return resource.kind === 'Pod' ||
+    (resource.spec && Array.isArray(resource.spec.containers));
+}
+
+/**
+ * Check if resource is a deployment
+ */
+export function isDeployment(resource: any): boolean {
+  return resource.kind === 'Deployment' || 
+         (resource.spec && typeof resource.spec.replicas !== 'undefined');
+}
+
+export function isStatefulSet(resource: any): boolean {
+  return resource.kind === 'StatefulSet';
+}
+
+export function isConfigMap(resource: any): boolean {
+  return resource.kind === 'ConfigMap';
+}
+

diff --git a/frontend/src/lib/terminal.ts b/frontend/src/lib/terminal.ts
line changes: +458/-0
index 0000000..e440585
--- /dev/null
+++ b/frontend/src/lib/terminal.ts
@@ -0,0 +1,458 @@
+import { Terminal } from '@xterm/xterm';
+import { FitAddon } from '@xterm/addon-fit';
+import '@xterm/xterm/css/xterm.css';
+
+export interface TerminalOptions {
+  contextName: string;
+  namespace: string;
+  podName: string;
+  containerName: string;
+}
+
+export class PodTerminal {
+  private terminal: Terminal | null = null;
+  private fitAddon: FitAddon | null = null;
+  private ws: WebSocket | null = null;
+  private container: HTMLElement | null = null;
+  private options: TerminalOptions;
+  private isReady: boolean = false;
+  private resizeHandler: (() => void) | null = null;
+  private reconnectAttempts: number = 0;
+  private maxReconnectAttempts: number = 3;
+  private connectTimeout: NodeJS.Timeout | null = null;
+  private pingInterval: NodeJS.Timeout | null = null;
+
+  constructor(options: TerminalOptions) {
+    this.options = options;
+  }
+
+  /**
+   * Initializes the terminal and connects to the pod
+   * @param containerId The ID of the HTML element to mount the terminal in
+   * @returns Promise that resolves when terminal is initialized
+   */
+  public async initialize(containerId: string): Promise<void> {
+    try {
+      // Get container element
+      this.container = document.getElementById(containerId);
+      if (!this.container) {
+        throw new Error(`Terminal container element with ID "${containerId}" not found`);
+      }
+
+      // Create terminal instance
+      this.terminal = new Terminal({
+        cursorBlink: true,
+        theme: {
+          background: '#1e1e1e',
+          foreground: '#e0e0e0',
+          cursor: '#ffffff',
+          selection: 'rgba(255, 255, 255, 0.3)',
+          black: '#000000',
+          red: '#e06c75',
+          green: '#98c379',
+          yellow: '#e5c07b',
+          blue: '#61afef',
+          magenta: '#c678dd',
+          cyan: '#56b6c2',
+          white: '#d0d0d0',
+          brightBlack: '#808080',
+          brightRed: '#f44747',
+          brightGreen: '#b5cea8',
+          brightYellow: '#dcdcaa',
+          brightBlue: '#569cd6',
+          brightMagenta: '#c586c0',
+          brightCyan: '#9cdcfe',
+          brightWhite: '#ffffff'
+        },
+        fontSize: 14,
+        fontFamily: 'Consolas, "Courier New", monospace',
+        scrollback: 1000,
+        convertEol: true
+      });
+
+      // Create fit addon
+      this.fitAddon = new FitAddon();
+      this.terminal.loadAddon(this.fitAddon);
+
+      // Open terminal in container
+      this.terminal.open(this.container);
+
+      // Show connecting message
+      this.terminal.writeln('Connecting to pod terminal...');
+      this.terminal.writeln('');
+
+      // Set up terminal input handler before connecting
+      this.terminal.onData((data) => {
+        // Send terminal input to the WebSocket
+        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+          this.ws.send(data);
+        }
+      });
+
+      // Connect to WebSocket
+      await this.connect();
+
+      // Fit terminal to container
+      this.fit();
+
+      // Focus terminal
+      this.terminal.focus();
+
+      // Set up resize handler
+      this.resizeHandler = this.fit.bind(this);
+      window.addEventListener('resize', this.resizeHandler);
+
+      // Mark as ready
+      this.isReady = true;
+    } catch (error) {
+      console.error('Error initializing terminal:', error);
+      if (this.terminal) {
+        this.terminal.writeln(`\r\nError initializing terminal: ${error}`);
+        this.terminal.writeln('Please check your connection and try again.');
+      }
+      throw error;
+    }
+  }
+
+  /**
+   * Connects to the pod terminal WebSocket
+   */
+  private async connect(): Promise<void> {
+    if (!this.terminal) {
+      throw new Error('Terminal not initialized');
+    }
+
+    return new Promise<void>((resolve, reject) => {
+      try {
+        // Close any existing connection
+        this.closeConnection();
+
+        // Determine the correct WebSocket URL based on the environment
+        let wsUrl: string;
+
+        // Check if we're running in a Wails app
+        if (typeof window !== 'undefined' && 'runtime' in window) {
+          // This is a Wails app, use the backend URL directly
+          wsUrl = `ws://127.0.0.1:8081/api/pods/exec?` + new URLSearchParams({
+            contextName: this.options.contextName,
+            namespace: this.options.namespace,
+            pod: this.options.podName,
+            container: this.options.containerName || ''
+          }).toString();
+        } else if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
+          // Local development
+          wsUrl = `ws://${window.location.host}/api/pods/exec?` + new URLSearchParams({
+            contextName: this.options.contextName,
+            namespace: this.options.namespace,
+            pod: this.options.podName,
+            container: this.options.containerName || ''
+          }).toString();
+        } else {
+          // Production or other environment
+          const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
+          wsUrl = `${protocol}//${window.location.host}/api/pods/exec?` + new URLSearchParams({
+            contextName: this.options.contextName,
+            namespace: this.options.namespace,
+            pod: this.options.podName,
+            container: this.options.containerName || ''
+          }).toString();
+        }
+
+        console.log(`Connecting to terminal WebSocket: ${wsUrl}`);
+        if (this.terminal) {
+          this.terminal.writeln(`Connecting to: ${this.options.podName}/${this.options.containerName || 'default'}`);
+        }
+
+        // Set a timeout for the connection
+        this.connectTimeout = setTimeout(() => {
+          if (this.ws && this.ws.readyState !== WebSocket.OPEN) {
+            console.error('WebSocket connection timeout');
+            if (this.terminal) {
+              this.terminal.writeln('\r\nConnection timeout. Server might be unavailable.');
+            }
+
+            // Force close the connection
+            if (this.ws) {
+              this.ws.close();
+            }
+
+            reject(new Error('WebSocket connection timeout'));
+          }
+        }, 15000); // 15 second timeout
+
+        // Create WebSocket connection
+        this.ws = new WebSocket(wsUrl);
+
+        // Set up event handlers
+        this.ws.onopen = () => {
+          console.log('Terminal WebSocket connection established');
+
+          // Clear the connection timeout
+          if (this.connectTimeout) {
+            clearTimeout(this.connectTimeout);
+            this.connectTimeout = null;
+          }
+
+          this.reconnectAttempts = 0;
+
+          if (this.terminal) {
+            // Clear the terminal
+            this.terminal.clear();
+
+            // Write welcome message
+            this.terminal.writeln('Connected to pod terminal.');
+            this.terminal.writeln('');
+
+            // Enable terminal input
+            this.terminal.options.disableStdin = false;
+
+            // Send initial terminal size
+            this.fit();
+
+            // Set up ping interval to keep connection alive
+            this.pingInterval = setInterval(() => {
+              if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+                // Send a special ping message (type 2)
+                const pingMessage = new Uint8Array([2]);
+                this.ws.send(pingMessage);
+              }
+            }, 30000); // 30 seconds
+          }
+
+          resolve();
+        };
+
+        this.ws.onmessage = (event) => {
+          // Handle different message types
+          if (event.data instanceof Blob) {
+            // Handle binary data
+            const reader = new FileReader();
+            reader.onload = () => {
+              if (this.terminal && reader.result) {
+                const data = new Uint8Array(reader.result as ArrayBuffer);
+                this.terminal.write(data);
+              }
+            };
+            reader.readAsArrayBuffer(event.data);
+          } else {
+            // Handle text data
+            if (this.terminal) {
+              this.terminal.write(event.data);
+            }
+          }
+        };
+
+        this.ws.onerror = (error) => {
+          console.error('Terminal WebSocket error:', error);
+
+          // Log detailed error information
+          console.log('WebSocket readyState:', this.ws ? this.ws.readyState : 'null');
+          console.log('WebSocket URL:', wsUrl);
+          console.log('Error event:', error);
+
+          if (this.terminal) {
+            this.terminal.writeln('\r\nConnection error. Please check your network and authentication.');
+            this.terminal.writeln(`Error details: WebSocket connection failed`);
+          }
+
+          // Don't reject immediately, let onclose handle it
+        };
+
+        this.ws.onclose = (event) => {
+          console.log(`Terminal WebSocket connection closed: ${event.code} ${event.reason}`);
+
+          // Clear timeouts and intervals
+          if (this.connectTimeout) {
+            clearTimeout(this.connectTimeout);
+            this.connectTimeout = null;
+          }
+
+          if (this.pingInterval) {
+            clearInterval(this.pingInterval);
+            this.pingInterval = null;
+          }
+
+          if (this.terminal) {
+            // Disable terminal input
+            this.terminal.options.disableStdin = true;
+
+            // Show connection closed message
+            this.terminal.writeln('\r\n');
+            this.terminal.writeln('Connection closed.');
+
+            if (event.code !== 1000 && event.code !== 1001) {
+              this.terminal.writeln(`Reason: ${event.reason || 'Server disconnected'} (Code: ${event.code})`);
+              this.terminal.writeln('');
+
+              // Try to auto-reconnect a few times
+              if (this.reconnectAttempts < this.maxReconnectAttempts) {
+                this.reconnectAttempts++;
+                this.terminal.writeln(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
+
+                setTimeout(() => {
+                  this.connect().catch(err => {
+                    console.error('Reconnect failed:', err);
+                    if (this.terminal) {
+                      this.terminal.writeln('Reconnect failed. Press the "Refresh" button to try again.');
+                    }
+                    reject(err);
+                  });
+                }, 2000);
+              } else {
+                this.terminal.writeln('Press the "Refresh" button to reconnect.');
+                reject(new Error(`WebSocket closed: ${event.code} ${event.reason}`));
+              }
+            } else {
+              resolve();
+            }
+          } else {
+            reject(new Error(`WebSocket closed: ${event.code} ${event.reason}`));
+          }
+        };
+      } catch (error) {
+        console.error('Error setting up terminal WebSocket:', error);
+        if (this.terminal) {
+          this.terminal.writeln(`\r\nError setting up connection: ${error}`);
+        }
+        reject(error);
+      }
+    });
+  }
+
+  /**
+   * Fits the terminal to its container
+   */
+  private fit(): void {
+    if (this.fitAddon && this.terminal) {
+      try {
+        this.fitAddon.fit();
+
+        // Send terminal size to server
+        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+          const cols = this.terminal.cols;
+          const rows = this.terminal.rows;
+
+          // Send resize message as binary data with a specific format:
+          // byte 1 = message type (1 for resize)
+          // byte 2 = rows (uint8)
+          // byte 3 = cols (uint8)
+          const resizeMessage = new Uint8Array([1, rows & 0xFF, cols & 0xFF]);
+          this.ws.send(resizeMessage);
+        }
+      } catch (error) {
+        console.error('Error resizing terminal:', error);
+      }
+    }
+  }
+
+  /**
+   * Closes the WebSocket connection
+   */
+  private closeConnection(): void {
+    // Clear any existing timeouts and intervals
+    if (this.connectTimeout) {
+      clearTimeout(this.connectTimeout);
+      this.connectTimeout = null;
+    }
+
+    if (this.pingInterval) {
+      clearInterval(this.pingInterval);
+      this.pingInterval = null;
+    }
+
+    if (this.ws) {
+      try {
+        console.log(`Closing WebSocket connection, current state: ${this.ws.readyState}`);
+
+        // Only close if not already closing/closed
+        if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
+          // For CONNECTING state, we need to set up onclose handler first
+          if (this.ws.readyState === WebSocket.CONNECTING) {
+            const oldOnClose = this.ws.onclose;
+            this.ws.onclose = (event) => {
+              console.log('Connection closed while in CONNECTING state');
+              if (oldOnClose) oldOnClose.call(this.ws, event);
+            };
+          }
+
+          this.ws.close(1000, 'User disconnected');
+        }
+      } catch (error) {
+        console.error('Error closing terminal WebSocket:', error);
+      }
+      this.ws = null;
+    }
+  }
+
+  /**
+   * Updates terminal options and reconnects
+   * @param options New terminal options
+   */
+  public async updateOptions(options: Partial<TerminalOptions>): Promise<void> {
+    // Update options
+    this.options = { ...this.options, ...options };
+
+    // Reconnect with new options
+    if (this.isReady) {
+      this.reconnectAttempts = 0;
+      if (this.terminal) {
+        this.terminal.writeln('\r\nUpdating connection...');
+      }
+      await this.connect();
+    }
+  }
+
+  /**
+   * Refreshes the terminal connection
+   */
+  public async refresh(): Promise<void> {
+    if (this.terminal) {
+      this.reconnectAttempts = 0;
+      this.terminal.writeln('\r\nRefreshing connection...');
+      await this.connect();
+      this.fit();
+      this.terminal.focus();
+    }
+  }
+
+  /**
+   * Disposes the terminal and cleans up resources
+   */
+  public dispose(): void {
+    // Remove resize event listener
+    if (this.resizeHandler) {
+      window.removeEventListener('resize', this.resizeHandler);
+      this.resizeHandler = null;
+    }
+
+    // Clear timeouts and intervals
+    if (this.connectTimeout) {
+      clearTimeout(this.connectTimeout);
+      this.connectTimeout = null;
+    }
+
+    if (this.pingInterval) {
+      clearInterval(this.pingInterval);
+      this.pingInterval = null;
+    }
+
+    // Close WebSocket connection
+    this.closeConnection();
+
+    // Dispose terminal
+    if (this.terminal) {
+      try {
+        this.terminal.dispose();
+      } catch (error) {
+        console.error('Error disposing terminal:', error);
+      }
+      this.terminal = null;
+    }
+
+    // Reset state
+    this.fitAddon = null;
+    this.isReady = false;
+    this.container = null;
+  }
+}
+

diff --git a/frontend/src/main.ts b/frontend/src/main.ts
line changes: +5/-0
index 0000000..f9754fe
--- /dev/null
+++ b/frontend/src/main.ts
@@ -0,0 +1,5 @@
+import {createApp} from 'vue'
+import App from './App.vue'
+import './style.css';
+
+createApp(App).mount('#app')

diff --git a/frontend/src/shims-vue.d.ts b/frontend/src/shims-vue.d.ts
line changes: +5/-0
index 0000000..2b97bd9
--- /dev/null
+++ b/frontend/src/shims-vue.d.ts
@@ -0,0 +1,5 @@
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue'
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}

diff --git a/frontend/src/style.css b/frontend/src/style.css
line changes: +26/-0
index 0000000..3940d6c
--- /dev/null
+++ b/frontend/src/style.css
@@ -0,0 +1,26 @@
+html {
+    background-color: rgba(27, 38, 54, 1);
+    text-align: center;
+    color: white;
+}
+
+body {
+    margin: 0;
+    color: white;
+    font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
+    "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+    sans-serif;
+}
+
+@font-face {
+    font-family: "Nunito";
+    font-style: normal;
+    font-weight: 400;
+    src: local(""),
+    url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2");
+}
+
+#app {
+    height: 100vh;
+    text-align: center;
+}

diff --git a/frontend/src/types/custom.ts b/frontend/src/types/custom.ts
line changes: +48/-0
index 0000000..a5b657e
--- /dev/null
+++ b/frontend/src/types/custom.ts
@@ -0,0 +1,48 @@
+export class CustomPod {
+  name: string;
+  image: string;
+  namespace: string;
+
+  constructor(name: string, image: string, namespace: string = 'default') {
+    this.name = name;
+    this.image = image;
+    this.namespace = namespace;
+  }
+
+  // Convert to Kubernetes Pod definition
+  toKubernetesPod(): any {
+    return {
+      apiVersion: 'v1',
+      kind: 'Pod',
+      metadata: {
+        name: this.name,
+        namespace: this.namespace
+      },
+      spec: {
+        containers: [
+          {
+            name: 'container-1',
+            image: this.image
+          }
+        ]
+      }
+    };
+  }
+
+  // Create from form data
+  static fromForm(name: string, image: string, namespace: string = 'default'): CustomPod {
+    return new CustomPod(name, image, namespace);
+  }
+}
+
+export interface SecretUpdateOptions {
+  context: string;
+  opts: string;
+  origNamespace: string;
+  origName: string;
+}
+
+export interface SecretCreateOptions {
+  context: string;
+  opts: any;
+}

diff --git a/frontend/src/types/kubernetes.ts b/frontend/src/types/kubernetes.ts
line changes: +73/-0
index 0000000..260c8c1
--- /dev/null
+++ b/frontend/src/types/kubernetes.ts
@@ -0,0 +1,73 @@
+import { 
+  V1Pod,
+  V1ConfigMap,
+  V1Deployment,
+  V1DeploymentSpec,
+  V1LabelSelector,
+  V1Ingress,
+  V1Namespace,
+  V1Secret,
+  V1PersistentVolumeClaim,
+  V1PersistentVolume,
+} from '@kubernetes/client-node';
+
+export interface ApiResponse<T> {
+  success: boolean;
+  msg: string;
+  data: T;
+}
+
+export interface KubernetesCluster {
+  contextName: string;
+}
+
+export interface KubernetesStatefulSet {
+  metadata: {
+    name: string;
+    namespace: string;
+    uid: string;
+    creationTimestamp: string;
+  };
+  spec: {
+    replicas: number;
+    strategy?: {
+      type: string;
+    };
+  };
+  status: {
+    availableReplicas: number;
+    readyReplicas: number;
+    replicas: number;
+    updatedReplicas: number;
+  };
+}
+
+export interface KubernetesDeployment extends Omit<V1Deployment, 'spec'> {
+  spec?: KubernetesDeploymentSpec;
+}
+
+interface KubernetesDeploymentSpec extends Omit<V1DeploymentSpec, 'selector'> {
+  selector?: V1LabelSelector;
+}
+
+export interface KubernetesPod extends V1Pod {
+}
+
+export interface KubernetesConfigMap extends Omit<V1ConfigMap, 'binaryData'> {
+  binaryData?: { [key: string]: number[] };
+}
+
+export interface KubernetesIngress extends V1Ingress {
+}
+
+export interface KubernetesNamespace extends V1Namespace {
+}
+
+export interface KubernetesSecret extends V1Secret {
+}
+
+export interface KubernetesPersistentVolumeClaim extends V1PersistentVolumeClaim{
+}
+
+export interface KubernetesPersistentVolume extends V1PersistentVolume{
+}

diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
line changes: +41/-0
index 0000000..05b4678
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,41 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "module": "esnext",
+    "strict": true,
+    "jsx": "preserve",
+    "moduleResolution": "node",
+    "skipLibCheck": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "forceConsistentCasingInFileNames": true,
+    "useDefineForClassFields": true,
+    "sourceMap": true,
+    "baseUrl": ".",
+    "types": [
+      "webpack-env",
+      "node"
+    ],
+    "paths": {
+      "@/*": [
+        "src/*"
+      ]
+    },
+    "lib": [
+      "esnext",
+      "dom",
+      "dom.iterable",
+      "scripthost"
+    ]
+  },
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue",
+    "tests/**/*.ts",
+    "tests/**/*.tsx"
+  ],
+  "exclude": [
+    "node_modules"
+  ]
+}

diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
line changes: +13/-0
index 0000000..8c4cb6d
--- /dev/null
+++ b/frontend/vite.config.ts
@@ -0,0 +1,13 @@
+import {defineConfig} from 'vite'
+import vue from '@vitejs/plugin-vue'
+const rootPath = new URL('.', import.meta.url).pathname
+
+export default defineConfig({
+  plugins: [vue()],
+  resolve: {
+    alias: {
+      '@': rootPath + 'src',
+      wailsjs: rootPath + 'wailsjs',
+    },
+  },
+})

diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts
line changes: +7444/-0
index 0000000..a6406dc
--- /dev/null
+++ b/frontend/wailsjs/go/models.ts
@@ -0,0 +1,7444 @@
+export namespace api {
+	
+	export class ExecEnvVar {
+	    name: string;
+	    value: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ExecEnvVar(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.value = source["value"];
+	    }
+	}
+	export class ExecConfig {
+	    command: string;
+	    args: string[];
+	    env: ExecEnvVar[];
+	    apiVersion?: string;
+	    installHint?: string;
+	    provideClusterInfo: boolean;
+	    interactiveMode?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ExecConfig(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.command = source["command"];
+	        this.args = source["args"];
+	        this.env = this.convertValues(source["env"], ExecEnvVar);
+	        this.apiVersion = source["apiVersion"];
+	        this.installHint = source["installHint"];
+	        this.provideClusterInfo = source["provideClusterInfo"];
+	        this.interactiveMode = source["interactiveMode"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class AuthProviderConfig {
+	    name: string;
+	    config?: Record<string, string>;
+	
+	    static createFrom(source: any = {}) {
+	        return new AuthProviderConfig(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.config = source["config"];
+	    }
+	}
+	export class AuthInfo {
+	    "client-certificate"?: string;
+	    "client-certificate-data"?: number[];
+	    "client-key"?: string;
+	    "client-key-data"?: number[];
+	    token?: string;
+	    tokenFile?: string;
+	    "act-as"?: string;
+	    "act-as-uid"?: string;
+	    "act-as-groups"?: string[];
+	    "act-as-user-extra"?: Record<string, string[]>;
+	    username?: string;
+	    password?: string;
+	    "auth-provider"?: AuthProviderConfig;
+	    exec?: ExecConfig;
+	    extensions?: Record<string, any>;
+	
+	    static createFrom(source: any = {}) {
+	        return new AuthInfo(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this["client-certificate"] = source["client-certificate"];
+	        this["client-certificate-data"] = source["client-certificate-data"];
+	        this["client-key"] = source["client-key"];
+	        this["client-key-data"] = source["client-key-data"];
+	        this.token = source["token"];
+	        this.tokenFile = source["tokenFile"];
+	        this["act-as"] = source["act-as"];
+	        this["act-as-uid"] = source["act-as-uid"];
+	        this["act-as-groups"] = source["act-as-groups"];
+	        this["act-as-user-extra"] = source["act-as-user-extra"];
+	        this.username = source["username"];
+	        this.password = source["password"];
+	        this["auth-provider"] = this.convertValues(source["auth-provider"], AuthProviderConfig);
+	        this.exec = this.convertValues(source["exec"], ExecConfig);
+	        this.extensions = source["extensions"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	export class Cluster {
+	    server: string;
+	    "tls-server-name"?: string;
+	    "insecure-skip-tls-verify"?: boolean;
+	    "certificate-authority"?: string;
+	    "certificate-authority-data"?: number[];
+	    "proxy-url"?: string;
+	    "disable-compression"?: boolean;
+	    extensions?: Record<string, any>;
+	
+	    static createFrom(source: any = {}) {
+	        return new Cluster(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.server = source["server"];
+	        this["tls-server-name"] = source["tls-server-name"];
+	        this["insecure-skip-tls-verify"] = source["insecure-skip-tls-verify"];
+	        this["certificate-authority"] = source["certificate-authority"];
+	        this["certificate-authority-data"] = source["certificate-authority-data"];
+	        this["proxy-url"] = source["proxy-url"];
+	        this["disable-compression"] = source["disable-compression"];
+	        this.extensions = source["extensions"];
+	    }
+	}
+	export class Context {
+	    cluster: string;
+	    user: string;
+	    namespace?: string;
+	    extensions?: Record<string, any>;
+	
+	    static createFrom(source: any = {}) {
+	        return new Context(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.cluster = source["cluster"];
+	        this.user = source["user"];
+	        this.namespace = source["namespace"];
+	        this.extensions = source["extensions"];
+	    }
+	}
+	export class Preferences {
+	    colors?: boolean;
+	    extensions?: Record<string, any>;
+	
+	    static createFrom(source: any = {}) {
+	        return new Preferences(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.colors = source["colors"];
+	        this.extensions = source["extensions"];
+	    }
+	}
+	export class Config {
+	    kind?: string;
+	    apiVersion?: string;
+	    preferences: Preferences;
+	    clusters: Record<string, Cluster>;
+	    users: Record<string, AuthInfo>;
+	    contexts: Record<string, Context>;
+	    "current-context": string;
+	    extensions?: Record<string, any>;
+	
+	    static createFrom(source: any = {}) {
+	        return new Config(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.preferences = this.convertValues(source["preferences"], Preferences);
+	        this.clusters = this.convertValues(source["clusters"], Cluster, true);
+	        this.users = this.convertValues(source["users"], AuthInfo, true);
+	        this.contexts = this.convertValues(source["contexts"], Context, true);
+	        this["current-context"] = source["current-context"];
+	        this.extensions = source["extensions"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+
+}
+
+export namespace config {
+	
+	export class KubeCluster {
+	    KubeContext?: api.Context;
+	    KubeCluster?: api.Cluster;
+	    KubeClientSet?: kubernetes.Clientset;
+	    KubeRestConfig?: rest.Config;
+	
+	    static createFrom(source: any = {}) {
+	        return new KubeCluster(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.KubeContext = this.convertValues(source["KubeContext"], api.Context);
+	        this.KubeCluster = this.convertValues(source["KubeCluster"], api.Cluster);
+	        this.KubeClientSet = this.convertValues(source["KubeClientSet"], kubernetes.Clientset);
+	        this.KubeRestConfig = this.convertValues(source["KubeRestConfig"], rest.Config);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class Config {
+	    KubeConfig: string;
+	    KubeConfigs: Record<string, api.Config>;
+	    DefaultKubeContext: string;
+	    KubeClusters: Record<string, KubeCluster>;
+	    Debug: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new Config(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.KubeConfig = source["KubeConfig"];
+	        this.KubeConfigs = this.convertValues(source["KubeConfigs"], api.Config, true);
+	        this.DefaultKubeContext = source["DefaultKubeContext"];
+	        this.KubeClusters = this.convertValues(source["KubeClusters"], KubeCluster, true);
+	        this.Debug = source["Debug"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+
+export namespace http {
+	
+	export class Response {
+	    Status: string;
+	    StatusCode: number;
+	    Proto: string;
+	    ProtoMajor: number;
+	    ProtoMinor: number;
+	    Header: Record<string, string[]>;
+	    Body: any;
+	    ContentLength: number;
+	    TransferEncoding: string[];
+	    Close: boolean;
+	    Uncompressed: boolean;
+	    Trailer: Record<string, string[]>;
+	    Request?: Request;
+	    TLS?: tls.ConnectionState;
+	
+	    static createFrom(source: any = {}) {
+	        return new Response(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Status = source["Status"];
+	        this.StatusCode = source["StatusCode"];
+	        this.Proto = source["Proto"];
+	        this.ProtoMajor = source["ProtoMajor"];
+	        this.ProtoMinor = source["ProtoMinor"];
+	        this.Header = source["Header"];
+	        this.Body = source["Body"];
+	        this.ContentLength = source["ContentLength"];
+	        this.TransferEncoding = source["TransferEncoding"];
+	        this.Close = source["Close"];
+	        this.Uncompressed = source["Uncompressed"];
+	        this.Trailer = source["Trailer"];
+	        this.Request = this.convertValues(source["Request"], Request);
+	        this.TLS = this.convertValues(source["TLS"], tls.ConnectionState);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class Request {
+	    Method: string;
+	    URL?: url.URL;
+	    Proto: string;
+	    ProtoMajor: number;
+	    ProtoMinor: number;
+	    Header: Record<string, string[]>;
+	    Body: any;
+	    ContentLength: number;
+	    TransferEncoding: string[];
+	    Close: boolean;
+	    Host: string;
+	    Form: Record<string, string[]>;
+	    PostForm: Record<string, string[]>;
+	    MultipartForm?: multipart.Form;
+	    Trailer: Record<string, string[]>;
+	    RemoteAddr: string;
+	    RequestURI: string;
+	    TLS?: tls.ConnectionState;
+	    Response?: Response;
+	    Pattern: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new Request(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Method = source["Method"];
+	        this.URL = this.convertValues(source["URL"], url.URL);
+	        this.Proto = source["Proto"];
+	        this.ProtoMajor = source["ProtoMajor"];
+	        this.ProtoMinor = source["ProtoMinor"];
+	        this.Header = source["Header"];
+	        this.Body = source["Body"];
+	        this.ContentLength = source["ContentLength"];
+	        this.TransferEncoding = source["TransferEncoding"];
+	        this.Close = source["Close"];
+	        this.Host = source["Host"];
+	        this.Form = source["Form"];
+	        this.PostForm = source["PostForm"];
+	        this.MultipartForm = this.convertValues(source["MultipartForm"], multipart.Form);
+	        this.Trailer = source["Trailer"];
+	        this.RemoteAddr = source["RemoteAddr"];
+	        this.RequestURI = source["RequestURI"];
+	        this.TLS = this.convertValues(source["TLS"], tls.ConnectionState);
+	        this.Response = this.convertValues(source["Response"], Response);
+	        this.Pattern = source["Pattern"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+
+export namespace intstr {
+	
+	export class IntOrString {
+	    Type: number;
+	    IntVal: number;
+	    StrVal: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new IntOrString(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Type = source["Type"];
+	        this.IntVal = source["IntVal"];
+	        this.StrVal = source["StrVal"];
+	    }
+	}
+
+}
+
+export namespace kubernetes {
+	
+	export class Clientset {
+	    LegacyPrefix: string;
+	    UseLegacyDiscovery: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new Clientset(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.LegacyPrefix = source["LegacyPrefix"];
+	        this.UseLegacyDiscovery = source["UseLegacyDiscovery"];
+	    }
+	}
+
+}
+
+export namespace logwrap {
+	
+	export class LogWrap {
+	
+	
+	    static createFrom(source: any = {}) {
+	        return new LogWrap(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	
+	    }
+	}
+
+}
+
+export namespace multipart {
+	
+	export class FileHeader {
+	    Filename: string;
+	    Header: Record<string, string[]>;
+	    Size: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new FileHeader(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Filename = source["Filename"];
+	        this.Header = source["Header"];
+	        this.Size = source["Size"];
+	    }
+	}
+	export class Form {
+	    Value: Record<string, string[]>;
+	    File: Record<string, FileHeader[]>;
+	
+	    static createFrom(source: any = {}) {
+	        return new Form(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Value = source["Value"];
+	        this.File = this.convertValues(source["File"], FileHeader[], true);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+
+export namespace net {
+	
+	export class IPNet {
+	    IP: number[];
+	    Mask: number[];
+	
+	    static createFrom(source: any = {}) {
+	        return new IPNet(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.IP = source["IP"];
+	        this.Mask = source["Mask"];
+	    }
+	}
+
+}
+
+export namespace pkix {
+	
+	export class AttributeTypeAndValue {
+	    Type: number[];
+	    Value: any;
+	
+	    static createFrom(source: any = {}) {
+	        return new AttributeTypeAndValue(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Type = source["Type"];
+	        this.Value = source["Value"];
+	    }
+	}
+	export class Extension {
+	    Id: number[];
+	    Critical: boolean;
+	    Value: number[];
+	
+	    static createFrom(source: any = {}) {
+	        return new Extension(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Id = source["Id"];
+	        this.Critical = source["Critical"];
+	        this.Value = source["Value"];
+	    }
+	}
+	export class Name {
+	    Country: string[];
+	    Organization: string[];
+	    OrganizationalUnit: string[];
+	    Locality: string[];
+	    Province: string[];
+	    StreetAddress: string[];
+	    PostalCode: string[];
+	    SerialNumber: string;
+	    CommonName: string;
+	    Names: AttributeTypeAndValue[];
+	    ExtraNames: AttributeTypeAndValue[];
+	
+	    static createFrom(source: any = {}) {
+	        return new Name(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Country = source["Country"];
+	        this.Organization = source["Organization"];
+	        this.OrganizationalUnit = source["OrganizationalUnit"];
+	        this.Locality = source["Locality"];
+	        this.Province = source["Province"];
+	        this.StreetAddress = source["StreetAddress"];
+	        this.PostalCode = source["PostalCode"];
+	        this.SerialNumber = source["SerialNumber"];
+	        this.CommonName = source["CommonName"];
+	        this.Names = this.convertValues(source["Names"], AttributeTypeAndValue);
+	        this.ExtraNames = this.convertValues(source["ExtraNames"], AttributeTypeAndValue);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+
+export namespace resource {
+	
+	export class Quantity {
+	    Format: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new Quantity(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Format = source["Format"];
+	    }
+	}
+
+}
+
+export namespace rest {
+	
+	export class ImpersonationConfig {
+	    UserName: string;
+	    UID: string;
+	    Groups: string[];
+	    Extra: Record<string, string[]>;
+	
+	    static createFrom(source: any = {}) {
+	        return new ImpersonationConfig(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.UserName = source["UserName"];
+	        this.UID = source["UID"];
+	        this.Groups = source["Groups"];
+	        this.Extra = source["Extra"];
+	    }
+	}
+	export class Config {
+	    Host: string;
+	    APIPath: string;
+	    AcceptContentTypes: string;
+	    ContentType: string;
+	    // Go type: schema
+	    GroupVersion?: any;
+	    NegotiatedSerializer: any;
+	    Username: string;
+	    Password: string;
+	    BearerToken: string;
+	    BearerTokenFile: string;
+	    Impersonate: ImpersonationConfig;
+	    AuthProvider?: api.AuthProviderConfig;
+	    AuthConfigPersister: any;
+	    ExecProvider?: api.ExecConfig;
+	    Insecure: boolean;
+	    ServerName: string;
+	    CertFile: string;
+	    KeyFile: string;
+	    CAFile: string;
+	    CertData: number[];
+	    KeyData: number[];
+	    CAData: number[];
+	    NextProtos: string[];
+	    UserAgent: string;
+	    DisableCompression: boolean;
+	    Transport: any;
+	    QPS: number;
+	    Burst: number;
+	    RateLimiter: any;
+	    WarningHandler: any;
+	    Timeout: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new Config(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Host = source["Host"];
+	        this.APIPath = source["APIPath"];
+	        this.AcceptContentTypes = source["AcceptContentTypes"];
+	        this.ContentType = source["ContentType"];
+	        this.GroupVersion = this.convertValues(source["GroupVersion"], null);
+	        this.NegotiatedSerializer = source["NegotiatedSerializer"];
+	        this.Username = source["Username"];
+	        this.Password = source["Password"];
+	        this.BearerToken = source["BearerToken"];
+	        this.BearerTokenFile = source["BearerTokenFile"];
+	        this.Impersonate = this.convertValues(source["Impersonate"], ImpersonationConfig);
+	        this.AuthProvider = this.convertValues(source["AuthProvider"], api.AuthProviderConfig);
+	        this.AuthConfigPersister = source["AuthConfigPersister"];
+	        this.ExecProvider = this.convertValues(source["ExecProvider"], api.ExecConfig);
+	        this.Insecure = source["Insecure"];
+	        this.ServerName = source["ServerName"];
+	        this.CertFile = source["CertFile"];
+	        this.KeyFile = source["KeyFile"];
+	        this.CAFile = source["CAFile"];
+	        this.CertData = source["CertData"];
+	        this.KeyData = source["KeyData"];
+	        this.CAData = source["CAData"];
+	        this.NextProtos = source["NextProtos"];
+	        this.UserAgent = source["UserAgent"];
+	        this.DisableCompression = source["DisableCompression"];
+	        this.Transport = source["Transport"];
+	        this.QPS = source["QPS"];
+	        this.Burst = source["Burst"];
+	        this.RateLimiter = source["RateLimiter"];
+	        this.WarningHandler = source["WarningHandler"];
+	        this.Timeout = source["Timeout"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+
+export namespace tls {
+	
+	export class ConnectionState {
+	    Version: number;
+	    HandshakeComplete: boolean;
+	    DidResume: boolean;
+	    CipherSuite: number;
+	    NegotiatedProtocol: string;
+	    NegotiatedProtocolIsMutual: boolean;
+	    ServerName: string;
+	    PeerCertificates: x509.Certificate[];
+	    VerifiedChains: x509.Certificate[][];
+	    SignedCertificateTimestamps: number[][];
+	    OCSPResponse: number[];
+	    TLSUnique: number[];
+	    ECHAccepted: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new ConnectionState(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Version = source["Version"];
+	        this.HandshakeComplete = source["HandshakeComplete"];
+	        this.DidResume = source["DidResume"];
+	        this.CipherSuite = source["CipherSuite"];
+	        this.NegotiatedProtocol = source["NegotiatedProtocol"];
+	        this.NegotiatedProtocolIsMutual = source["NegotiatedProtocolIsMutual"];
+	        this.ServerName = source["ServerName"];
+	        this.PeerCertificates = this.convertValues(source["PeerCertificates"], x509.Certificate);
+	        this.VerifiedChains = this.convertValues(source["VerifiedChains"], x509.Certificate);
+	        this.SignedCertificateTimestamps = source["SignedCertificateTimestamps"];
+	        this.OCSPResponse = source["OCSPResponse"];
+	        this.TLSUnique = source["TLSUnique"];
+	        this.ECHAccepted = source["ECHAccepted"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+
+export namespace types {
+	
+	export class Cluster {
+	    contextName: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new Cluster(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.contextName = source["contextName"];
+	    }
+	}
+	export class ClustersResponse {
+	    success: boolean;
+	    msg: string;
+	    data: Cluster[];
+	
+	    static createFrom(source: any = {}) {
+	        return new ClustersResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], Cluster);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ConfigMapsResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.ConfigMap[];
+	
+	    static createFrom(source: any = {}) {
+	        return new ConfigMapsResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.ConfigMap);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class DeploymentResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Deployment;
+	
+	    static createFrom(source: any = {}) {
+	        return new DeploymentResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Deployment);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class DeploymentsResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Deployment[];
+	
+	    static createFrom(source: any = {}) {
+	        return new DeploymentsResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Deployment);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class DescribeResponse {
+	    success: boolean;
+	    msg: string;
+	    data: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new DescribeResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = source["data"];
+	    }
+	}
+	export class IngressesResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Ingress[];
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressesResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Ingress);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class NamespaceResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Namespace;
+	
+	    static createFrom(source: any = {}) {
+	        return new NamespaceResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Namespace);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class NamespacesResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Namespace[];
+	
+	    static createFrom(source: any = {}) {
+	        return new NamespacesResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Namespace);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumeClaimResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.PersistentVolumeClaim;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeClaimResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.PersistentVolumeClaim);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumeClaimsResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.PersistentVolumeClaim[];
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeClaimsResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.PersistentVolumeClaim);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumesResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.PersistentVolume[];
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumesResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.PersistentVolume);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Pod;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Pod);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodsResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Pod[];
+	
+	    static createFrom(source: any = {}) {
+	        return new PodsResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Pod);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class Response {
+	    success: boolean;
+	    msg: string;
+	    data: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new Response(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = source["data"];
+	    }
+	}
+	export class SecretResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Secret;
+	
+	    static createFrom(source: any = {}) {
+	        return new SecretResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Secret);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class SecretsResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.Secret[];
+	
+	    static createFrom(source: any = {}) {
+	        return new SecretsResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.Secret);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class StatefulSetResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.StatefulSet;
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSetResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.StatefulSet);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class StatefulSetsResponse {
+	    success: boolean;
+	    msg: string;
+	    data: v1.StatefulSet[];
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSetsResponse(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.success = source["success"];
+	        this.msg = source["msg"];
+	        this.data = this.convertValues(source["data"], v1.StatefulSet);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+
+export namespace url {
+	
+	export class Userinfo {
+	
+	
+	    static createFrom(source: any = {}) {
+	        return new Userinfo(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	
+	    }
+	}
+	export class URL {
+	    Scheme: string;
+	    Opaque: string;
+	    // Go type: Userinfo
+	    User?: any;
+	    Host: string;
+	    Path: string;
+	    RawPath: string;
+	    OmitHost: boolean;
+	    ForceQuery: boolean;
+	    RawQuery: string;
+	    Fragment: string;
+	    RawFragment: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new URL(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Scheme = source["Scheme"];
+	        this.Opaque = source["Opaque"];
+	        this.User = this.convertValues(source["User"], null);
+	        this.Host = source["Host"];
+	        this.Path = source["Path"];
+	        this.RawPath = source["RawPath"];
+	        this.OmitHost = source["OmitHost"];
+	        this.ForceQuery = source["ForceQuery"];
+	        this.RawQuery = source["RawQuery"];
+	        this.Fragment = source["Fragment"];
+	        this.RawFragment = source["RawFragment"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+
+export namespace v1 {
+	
+	export class PodAntiAffinity {
+	    requiredDuringSchedulingIgnoredDuringExecution?: PodAffinityTerm[];
+	    preferredDuringSchedulingIgnoredDuringExecution?: WeightedPodAffinityTerm[];
+	
+	    static createFrom(source: any = {}) {
+	        return new PodAntiAffinity(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.requiredDuringSchedulingIgnoredDuringExecution = this.convertValues(source["requiredDuringSchedulingIgnoredDuringExecution"], PodAffinityTerm);
+	        this.preferredDuringSchedulingIgnoredDuringExecution = this.convertValues(source["preferredDuringSchedulingIgnoredDuringExecution"], WeightedPodAffinityTerm);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class WeightedPodAffinityTerm {
+	    weight: number;
+	    podAffinityTerm: PodAffinityTerm;
+	
+	    static createFrom(source: any = {}) {
+	        return new WeightedPodAffinityTerm(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.weight = source["weight"];
+	        this.podAffinityTerm = this.convertValues(source["podAffinityTerm"], PodAffinityTerm);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class LabelSelectorRequirement {
+	    key: string;
+	    operator: string;
+	    values?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new LabelSelectorRequirement(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.key = source["key"];
+	        this.operator = source["operator"];
+	        this.values = source["values"];
+	    }
+	}
+	export class LabelSelector {
+	    matchLabels?: Record<string, string>;
+	    matchExpressions?: LabelSelectorRequirement[];
+	
+	    static createFrom(source: any = {}) {
+	        return new LabelSelector(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.matchLabels = source["matchLabels"];
+	        this.matchExpressions = this.convertValues(source["matchExpressions"], LabelSelectorRequirement);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodAffinityTerm {
+	    labelSelector?: LabelSelector;
+	    namespaces?: string[];
+	    topologyKey: string;
+	    namespaceSelector?: LabelSelector;
+	    matchLabelKeys?: string[];
+	    mismatchLabelKeys?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new PodAffinityTerm(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.labelSelector = this.convertValues(source["labelSelector"], LabelSelector);
+	        this.namespaces = source["namespaces"];
+	        this.topologyKey = source["topologyKey"];
+	        this.namespaceSelector = this.convertValues(source["namespaceSelector"], LabelSelector);
+	        this.matchLabelKeys = source["matchLabelKeys"];
+	        this.mismatchLabelKeys = source["mismatchLabelKeys"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodAffinity {
+	    requiredDuringSchedulingIgnoredDuringExecution?: PodAffinityTerm[];
+	    preferredDuringSchedulingIgnoredDuringExecution?: WeightedPodAffinityTerm[];
+	
+	    static createFrom(source: any = {}) {
+	        return new PodAffinity(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.requiredDuringSchedulingIgnoredDuringExecution = this.convertValues(source["requiredDuringSchedulingIgnoredDuringExecution"], PodAffinityTerm);
+	        this.preferredDuringSchedulingIgnoredDuringExecution = this.convertValues(source["preferredDuringSchedulingIgnoredDuringExecution"], WeightedPodAffinityTerm);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PreferredSchedulingTerm {
+	    weight: number;
+	    preference: NodeSelectorTerm;
+	
+	    static createFrom(source: any = {}) {
+	        return new PreferredSchedulingTerm(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.weight = source["weight"];
+	        this.preference = this.convertValues(source["preference"], NodeSelectorTerm);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class NodeSelectorRequirement {
+	    key: string;
+	    operator: string;
+	    values?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new NodeSelectorRequirement(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.key = source["key"];
+	        this.operator = source["operator"];
+	        this.values = source["values"];
+	    }
+	}
+	export class NodeSelectorTerm {
+	    matchExpressions?: NodeSelectorRequirement[];
+	    matchFields?: NodeSelectorRequirement[];
+	
+	    static createFrom(source: any = {}) {
+	        return new NodeSelectorTerm(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.matchExpressions = this.convertValues(source["matchExpressions"], NodeSelectorRequirement);
+	        this.matchFields = this.convertValues(source["matchFields"], NodeSelectorRequirement);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class NodeSelector {
+	    nodeSelectorTerms: NodeSelectorTerm[];
+	
+	    static createFrom(source: any = {}) {
+	        return new NodeSelector(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.nodeSelectorTerms = this.convertValues(source["nodeSelectorTerms"], NodeSelectorTerm);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class NodeAffinity {
+	    requiredDuringSchedulingIgnoredDuringExecution?: NodeSelector;
+	    preferredDuringSchedulingIgnoredDuringExecution?: PreferredSchedulingTerm[];
+	
+	    static createFrom(source: any = {}) {
+	        return new NodeAffinity(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.requiredDuringSchedulingIgnoredDuringExecution = this.convertValues(source["requiredDuringSchedulingIgnoredDuringExecution"], NodeSelector);
+	        this.preferredDuringSchedulingIgnoredDuringExecution = this.convertValues(source["preferredDuringSchedulingIgnoredDuringExecution"], PreferredSchedulingTerm);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class Affinity {
+	    nodeAffinity?: NodeAffinity;
+	    podAffinity?: PodAffinity;
+	    podAntiAffinity?: PodAntiAffinity;
+	
+	    static createFrom(source: any = {}) {
+	        return new Affinity(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.nodeAffinity = this.convertValues(source["nodeAffinity"], NodeAffinity);
+	        this.podAffinity = this.convertValues(source["podAffinity"], PodAffinity);
+	        this.podAntiAffinity = this.convertValues(source["podAntiAffinity"], PodAntiAffinity);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class AppArmorProfile {
+	    type: string;
+	    localhostProfile?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new AppArmorProfile(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.localhostProfile = source["localhostProfile"];
+	    }
+	}
+	export class Capabilities {
+	    add?: string[];
+	    drop?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new Capabilities(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.add = source["add"];
+	        this.drop = source["drop"];
+	    }
+	}
+	export class FieldsV1 {
+	
+	
+	    static createFrom(source: any = {}) {
+	        return new FieldsV1(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	
+	    }
+	}
+	export class ManagedFieldsEntry {
+	    manager?: string;
+	    operation?: string;
+	    apiVersion?: string;
+	    time?: Time;
+	    fieldsType?: string;
+	    // Go type: FieldsV1
+	    fieldsV1?: any;
+	    subresource?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ManagedFieldsEntry(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.manager = source["manager"];
+	        this.operation = source["operation"];
+	        this.apiVersion = source["apiVersion"];
+	        this.time = this.convertValues(source["time"], Time);
+	        this.fieldsType = source["fieldsType"];
+	        this.fieldsV1 = this.convertValues(source["fieldsV1"], null);
+	        this.subresource = source["subresource"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class OwnerReference {
+	    apiVersion: string;
+	    kind: string;
+	    name: string;
+	    uid: string;
+	    controller?: boolean;
+	    blockOwnerDeletion?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new OwnerReference(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.apiVersion = source["apiVersion"];
+	        this.kind = source["kind"];
+	        this.name = source["name"];
+	        this.uid = source["uid"];
+	        this.controller = source["controller"];
+	        this.blockOwnerDeletion = source["blockOwnerDeletion"];
+	    }
+	}
+	export class Time {
+	
+	
+	    static createFrom(source: any = {}) {
+	        return new Time(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	
+	    }
+	}
+	export class ConfigMap {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    immutable?: boolean;
+	    data?: Record<string, string>;
+	    binaryData?: Record<string, number[]>;
+	
+	    static createFrom(source: any = {}) {
+	        return new ConfigMap(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.immutable = source["immutable"];
+	        this.data = source["data"];
+	        this.binaryData = source["binaryData"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ConfigMapEnvSource {
+	    name?: string;
+	    optional?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new ConfigMapEnvSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.optional = source["optional"];
+	    }
+	}
+	export class ConfigMapKeySelector {
+	    name?: string;
+	    key: string;
+	    optional?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new ConfigMapKeySelector(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.key = source["key"];
+	        this.optional = source["optional"];
+	    }
+	}
+	export class SeccompProfile {
+	    type: string;
+	    localhostProfile?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new SeccompProfile(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.localhostProfile = source["localhostProfile"];
+	    }
+	}
+	export class WindowsSecurityContextOptions {
+	    gmsaCredentialSpecName?: string;
+	    gmsaCredentialSpec?: string;
+	    runAsUserName?: string;
+	    hostProcess?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new WindowsSecurityContextOptions(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.gmsaCredentialSpecName = source["gmsaCredentialSpecName"];
+	        this.gmsaCredentialSpec = source["gmsaCredentialSpec"];
+	        this.runAsUserName = source["runAsUserName"];
+	        this.hostProcess = source["hostProcess"];
+	    }
+	}
+	export class SELinuxOptions {
+	    user?: string;
+	    role?: string;
+	    type?: string;
+	    level?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new SELinuxOptions(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.user = source["user"];
+	        this.role = source["role"];
+	        this.type = source["type"];
+	        this.level = source["level"];
+	    }
+	}
+	export class SecurityContext {
+	    capabilities?: Capabilities;
+	    privileged?: boolean;
+	    seLinuxOptions?: SELinuxOptions;
+	    windowsOptions?: WindowsSecurityContextOptions;
+	    runAsUser?: number;
+	    runAsGroup?: number;
+	    runAsNonRoot?: boolean;
+	    readOnlyRootFilesystem?: boolean;
+	    allowPrivilegeEscalation?: boolean;
+	    procMount?: string;
+	    seccompProfile?: SeccompProfile;
+	    appArmorProfile?: AppArmorProfile;
+	
+	    static createFrom(source: any = {}) {
+	        return new SecurityContext(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.capabilities = this.convertValues(source["capabilities"], Capabilities);
+	        this.privileged = source["privileged"];
+	        this.seLinuxOptions = this.convertValues(source["seLinuxOptions"], SELinuxOptions);
+	        this.windowsOptions = this.convertValues(source["windowsOptions"], WindowsSecurityContextOptions);
+	        this.runAsUser = source["runAsUser"];
+	        this.runAsGroup = source["runAsGroup"];
+	        this.runAsNonRoot = source["runAsNonRoot"];
+	        this.readOnlyRootFilesystem = source["readOnlyRootFilesystem"];
+	        this.allowPrivilegeEscalation = source["allowPrivilegeEscalation"];
+	        this.procMount = source["procMount"];
+	        this.seccompProfile = this.convertValues(source["seccompProfile"], SeccompProfile);
+	        this.appArmorProfile = this.convertValues(source["appArmorProfile"], AppArmorProfile);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class SleepAction {
+	    seconds: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new SleepAction(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.seconds = source["seconds"];
+	    }
+	}
+	export class LifecycleHandler {
+	    exec?: ExecAction;
+	    httpGet?: HTTPGetAction;
+	    tcpSocket?: TCPSocketAction;
+	    sleep?: SleepAction;
+	
+	    static createFrom(source: any = {}) {
+	        return new LifecycleHandler(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.exec = this.convertValues(source["exec"], ExecAction);
+	        this.httpGet = this.convertValues(source["httpGet"], HTTPGetAction);
+	        this.tcpSocket = this.convertValues(source["tcpSocket"], TCPSocketAction);
+	        this.sleep = this.convertValues(source["sleep"], SleepAction);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class Lifecycle {
+	    postStart?: LifecycleHandler;
+	    preStop?: LifecycleHandler;
+	
+	    static createFrom(source: any = {}) {
+	        return new Lifecycle(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.postStart = this.convertValues(source["postStart"], LifecycleHandler);
+	        this.preStop = this.convertValues(source["preStop"], LifecycleHandler);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class GRPCAction {
+	    port: number;
+	    service?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new GRPCAction(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.port = source["port"];
+	        this.service = source["service"];
+	    }
+	}
+	export class TCPSocketAction {
+	    port: intstr.IntOrString;
+	    host?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new TCPSocketAction(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.port = this.convertValues(source["port"], intstr.IntOrString);
+	        this.host = source["host"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class HTTPHeader {
+	    name: string;
+	    value: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new HTTPHeader(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.value = source["value"];
+	    }
+	}
+	export class HTTPGetAction {
+	    path?: string;
+	    port: intstr.IntOrString;
+	    host?: string;
+	    scheme?: string;
+	    httpHeaders?: HTTPHeader[];
+	
+	    static createFrom(source: any = {}) {
+	        return new HTTPGetAction(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.path = source["path"];
+	        this.port = this.convertValues(source["port"], intstr.IntOrString);
+	        this.host = source["host"];
+	        this.scheme = source["scheme"];
+	        this.httpHeaders = this.convertValues(source["httpHeaders"], HTTPHeader);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ExecAction {
+	    command?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new ExecAction(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.command = source["command"];
+	    }
+	}
+	export class Probe {
+	    exec?: ExecAction;
+	    httpGet?: HTTPGetAction;
+	    tcpSocket?: TCPSocketAction;
+	    // Go type: GRPCAction
+	    grpc?: any;
+	    initialDelaySeconds?: number;
+	    timeoutSeconds?: number;
+	    periodSeconds?: number;
+	    successThreshold?: number;
+	    failureThreshold?: number;
+	    terminationGracePeriodSeconds?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new Probe(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.exec = this.convertValues(source["exec"], ExecAction);
+	        this.httpGet = this.convertValues(source["httpGet"], HTTPGetAction);
+	        this.tcpSocket = this.convertValues(source["tcpSocket"], TCPSocketAction);
+	        this.grpc = this.convertValues(source["grpc"], null);
+	        this.initialDelaySeconds = source["initialDelaySeconds"];
+	        this.timeoutSeconds = source["timeoutSeconds"];
+	        this.periodSeconds = source["periodSeconds"];
+	        this.successThreshold = source["successThreshold"];
+	        this.failureThreshold = source["failureThreshold"];
+	        this.terminationGracePeriodSeconds = source["terminationGracePeriodSeconds"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class VolumeDevice {
+	    name: string;
+	    devicePath: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new VolumeDevice(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.devicePath = source["devicePath"];
+	    }
+	}
+	export class VolumeMount {
+	    name: string;
+	    readOnly?: boolean;
+	    recursiveReadOnly?: string;
+	    mountPath: string;
+	    subPath?: string;
+	    mountPropagation?: string;
+	    subPathExpr?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new VolumeMount(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.readOnly = source["readOnly"];
+	        this.recursiveReadOnly = source["recursiveReadOnly"];
+	        this.mountPath = source["mountPath"];
+	        this.subPath = source["subPath"];
+	        this.mountPropagation = source["mountPropagation"];
+	        this.subPathExpr = source["subPathExpr"];
+	    }
+	}
+	export class ContainerResizePolicy {
+	    resourceName: string;
+	    restartPolicy: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ContainerResizePolicy(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.resourceName = source["resourceName"];
+	        this.restartPolicy = source["restartPolicy"];
+	    }
+	}
+	export class ResourceClaim {
+	    name: string;
+	    request?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ResourceClaim(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.request = source["request"];
+	    }
+	}
+	export class ResourceRequirements {
+	    limits?: Record<string, resource.Quantity>;
+	    requests?: Record<string, resource.Quantity>;
+	    claims?: ResourceClaim[];
+	
+	    static createFrom(source: any = {}) {
+	        return new ResourceRequirements(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.limits = this.convertValues(source["limits"], resource.Quantity, true);
+	        this.requests = this.convertValues(source["requests"], resource.Quantity, true);
+	        this.claims = this.convertValues(source["claims"], ResourceClaim);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class SecretKeySelector {
+	    name?: string;
+	    key: string;
+	    optional?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new SecretKeySelector(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.key = source["key"];
+	        this.optional = source["optional"];
+	    }
+	}
+	export class ResourceFieldSelector {
+	    containerName?: string;
+	    resource: string;
+	    divisor?: resource.Quantity;
+	
+	    static createFrom(source: any = {}) {
+	        return new ResourceFieldSelector(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.containerName = source["containerName"];
+	        this.resource = source["resource"];
+	        this.divisor = this.convertValues(source["divisor"], resource.Quantity);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ObjectFieldSelector {
+	    apiVersion?: string;
+	    fieldPath: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ObjectFieldSelector(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.apiVersion = source["apiVersion"];
+	        this.fieldPath = source["fieldPath"];
+	    }
+	}
+	export class EnvVarSource {
+	    fieldRef?: ObjectFieldSelector;
+	    resourceFieldRef?: ResourceFieldSelector;
+	    configMapKeyRef?: ConfigMapKeySelector;
+	    secretKeyRef?: SecretKeySelector;
+	
+	    static createFrom(source: any = {}) {
+	        return new EnvVarSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.fieldRef = this.convertValues(source["fieldRef"], ObjectFieldSelector);
+	        this.resourceFieldRef = this.convertValues(source["resourceFieldRef"], ResourceFieldSelector);
+	        this.configMapKeyRef = this.convertValues(source["configMapKeyRef"], ConfigMapKeySelector);
+	        this.secretKeyRef = this.convertValues(source["secretKeyRef"], SecretKeySelector);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class EnvVar {
+	    name: string;
+	    value?: string;
+	    valueFrom?: EnvVarSource;
+	
+	    static createFrom(source: any = {}) {
+	        return new EnvVar(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.value = source["value"];
+	        this.valueFrom = this.convertValues(source["valueFrom"], EnvVarSource);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class SecretEnvSource {
+	    name?: string;
+	    optional?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new SecretEnvSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.optional = source["optional"];
+	    }
+	}
+	export class EnvFromSource {
+	    prefix?: string;
+	    configMapRef?: ConfigMapEnvSource;
+	    secretRef?: SecretEnvSource;
+	
+	    static createFrom(source: any = {}) {
+	        return new EnvFromSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.prefix = source["prefix"];
+	        this.configMapRef = this.convertValues(source["configMapRef"], ConfigMapEnvSource);
+	        this.secretRef = this.convertValues(source["secretRef"], SecretEnvSource);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ContainerPort {
+	    name?: string;
+	    hostPort?: number;
+	    containerPort: number;
+	    protocol?: string;
+	    hostIP?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ContainerPort(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.hostPort = source["hostPort"];
+	        this.containerPort = source["containerPort"];
+	        this.protocol = source["protocol"];
+	        this.hostIP = source["hostIP"];
+	    }
+	}
+	export class Container {
+	    name: string;
+	    image?: string;
+	    command?: string[];
+	    args?: string[];
+	    workingDir?: string;
+	    ports?: ContainerPort[];
+	    envFrom?: EnvFromSource[];
+	    env?: EnvVar[];
+	    resources?: ResourceRequirements;
+	    resizePolicy?: ContainerResizePolicy[];
+	    restartPolicy?: string;
+	    volumeMounts?: VolumeMount[];
+	    volumeDevices?: VolumeDevice[];
+	    livenessProbe?: Probe;
+	    readinessProbe?: Probe;
+	    startupProbe?: Probe;
+	    lifecycle?: Lifecycle;
+	    terminationMessagePath?: string;
+	    terminationMessagePolicy?: string;
+	    imagePullPolicy?: string;
+	    securityContext?: SecurityContext;
+	    stdin?: boolean;
+	    stdinOnce?: boolean;
+	    tty?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new Container(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.image = source["image"];
+	        this.command = source["command"];
+	        this.args = source["args"];
+	        this.workingDir = source["workingDir"];
+	        this.ports = this.convertValues(source["ports"], ContainerPort);
+	        this.envFrom = this.convertValues(source["envFrom"], EnvFromSource);
+	        this.env = this.convertValues(source["env"], EnvVar);
+	        this.resources = this.convertValues(source["resources"], ResourceRequirements);
+	        this.resizePolicy = this.convertValues(source["resizePolicy"], ContainerResizePolicy);
+	        this.restartPolicy = source["restartPolicy"];
+	        this.volumeMounts = this.convertValues(source["volumeMounts"], VolumeMount);
+	        this.volumeDevices = this.convertValues(source["volumeDevices"], VolumeDevice);
+	        this.livenessProbe = this.convertValues(source["livenessProbe"], Probe);
+	        this.readinessProbe = this.convertValues(source["readinessProbe"], Probe);
+	        this.startupProbe = this.convertValues(source["startupProbe"], Probe);
+	        this.lifecycle = this.convertValues(source["lifecycle"], Lifecycle);
+	        this.terminationMessagePath = source["terminationMessagePath"];
+	        this.terminationMessagePolicy = source["terminationMessagePolicy"];
+	        this.imagePullPolicy = source["imagePullPolicy"];
+	        this.securityContext = this.convertValues(source["securityContext"], SecurityContext);
+	        this.stdin = source["stdin"];
+	        this.stdinOnce = source["stdinOnce"];
+	        this.tty = source["tty"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	export class ContainerStateTerminated {
+	    exitCode: number;
+	    signal?: number;
+	    reason?: string;
+	    message?: string;
+	    startedAt?: Time;
+	    finishedAt?: Time;
+	    containerID?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ContainerStateTerminated(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.exitCode = source["exitCode"];
+	        this.signal = source["signal"];
+	        this.reason = source["reason"];
+	        this.message = source["message"];
+	        this.startedAt = this.convertValues(source["startedAt"], Time);
+	        this.finishedAt = this.convertValues(source["finishedAt"], Time);
+	        this.containerID = source["containerID"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ContainerStateRunning {
+	    startedAt?: Time;
+	
+	    static createFrom(source: any = {}) {
+	        return new ContainerStateRunning(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.startedAt = this.convertValues(source["startedAt"], Time);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ContainerStateWaiting {
+	    reason?: string;
+	    message?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ContainerStateWaiting(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.reason = source["reason"];
+	        this.message = source["message"];
+	    }
+	}
+	export class ContainerState {
+	    waiting?: ContainerStateWaiting;
+	    running?: ContainerStateRunning;
+	    terminated?: ContainerStateTerminated;
+	
+	    static createFrom(source: any = {}) {
+	        return new ContainerState(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.waiting = this.convertValues(source["waiting"], ContainerStateWaiting);
+	        this.running = this.convertValues(source["running"], ContainerStateRunning);
+	        this.terminated = this.convertValues(source["terminated"], ContainerStateTerminated);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+	export class ResourceHealth {
+	    resourceID: string;
+	    health?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ResourceHealth(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.resourceID = source["resourceID"];
+	        this.health = source["health"];
+	    }
+	}
+	export class ResourceStatus {
+	    name: string;
+	    resources?: ResourceHealth[];
+	
+	    static createFrom(source: any = {}) {
+	        return new ResourceStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.resources = this.convertValues(source["resources"], ResourceHealth);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class LinuxContainerUser {
+	    uid: number;
+	    gid: number;
+	    supplementalGroups?: number[];
+	
+	    static createFrom(source: any = {}) {
+	        return new LinuxContainerUser(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.uid = source["uid"];
+	        this.gid = source["gid"];
+	        this.supplementalGroups = source["supplementalGroups"];
+	    }
+	}
+	export class ContainerUser {
+	    linux?: LinuxContainerUser;
+	
+	    static createFrom(source: any = {}) {
+	        return new ContainerUser(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.linux = this.convertValues(source["linux"], LinuxContainerUser);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class VolumeMountStatus {
+	    name: string;
+	    mountPath: string;
+	    readOnly?: boolean;
+	    recursiveReadOnly?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new VolumeMountStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.mountPath = source["mountPath"];
+	        this.readOnly = source["readOnly"];
+	        this.recursiveReadOnly = source["recursiveReadOnly"];
+	    }
+	}
+	export class ContainerStatus {
+	    name: string;
+	    state?: ContainerState;
+	    lastState?: ContainerState;
+	    ready: boolean;
+	    restartCount: number;
+	    image: string;
+	    imageID: string;
+	    containerID?: string;
+	    started?: boolean;
+	    allocatedResources?: Record<string, resource.Quantity>;
+	    resources?: ResourceRequirements;
+	    volumeMounts?: VolumeMountStatus[];
+	    user?: ContainerUser;
+	    allocatedResourcesStatus?: ResourceStatus[];
+	
+	    static createFrom(source: any = {}) {
+	        return new ContainerStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.state = this.convertValues(source["state"], ContainerState);
+	        this.lastState = this.convertValues(source["lastState"], ContainerState);
+	        this.ready = source["ready"];
+	        this.restartCount = source["restartCount"];
+	        this.image = source["image"];
+	        this.imageID = source["imageID"];
+	        this.containerID = source["containerID"];
+	        this.started = source["started"];
+	        this.allocatedResources = this.convertValues(source["allocatedResources"], resource.Quantity, true);
+	        this.resources = this.convertValues(source["resources"], ResourceRequirements);
+	        this.volumeMounts = this.convertValues(source["volumeMounts"], VolumeMountStatus);
+	        this.user = this.convertValues(source["user"], ContainerUser);
+	        this.allocatedResourcesStatus = this.convertValues(source["allocatedResourcesStatus"], ResourceStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	export class DeploymentCondition {
+	    type: string;
+	    status: string;
+	    lastUpdateTime?: Time;
+	    lastTransitionTime?: Time;
+	    reason?: string;
+	    message?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new DeploymentCondition(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.status = source["status"];
+	        this.lastUpdateTime = this.convertValues(source["lastUpdateTime"], Time);
+	        this.lastTransitionTime = this.convertValues(source["lastTransitionTime"], Time);
+	        this.reason = source["reason"];
+	        this.message = source["message"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class DeploymentStatus {
+	    observedGeneration?: number;
+	    replicas?: number;
+	    updatedReplicas?: number;
+	    readyReplicas?: number;
+	    availableReplicas?: number;
+	    unavailableReplicas?: number;
+	    conditions?: DeploymentCondition[];
+	    collisionCount?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new DeploymentStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.observedGeneration = source["observedGeneration"];
+	        this.replicas = source["replicas"];
+	        this.updatedReplicas = source["updatedReplicas"];
+	        this.readyReplicas = source["readyReplicas"];
+	        this.availableReplicas = source["availableReplicas"];
+	        this.unavailableReplicas = source["unavailableReplicas"];
+	        this.conditions = this.convertValues(source["conditions"], DeploymentCondition);
+	        this.collisionCount = source["collisionCount"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class RollingUpdateDeployment {
+	    maxUnavailable?: intstr.IntOrString;
+	    maxSurge?: intstr.IntOrString;
+	
+	    static createFrom(source: any = {}) {
+	        return new RollingUpdateDeployment(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.maxUnavailable = this.convertValues(source["maxUnavailable"], intstr.IntOrString);
+	        this.maxSurge = this.convertValues(source["maxSurge"], intstr.IntOrString);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class DeploymentStrategy {
+	    type?: string;
+	    rollingUpdate?: RollingUpdateDeployment;
+	
+	    static createFrom(source: any = {}) {
+	        return new DeploymentStrategy(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.rollingUpdate = this.convertValues(source["rollingUpdate"], RollingUpdateDeployment);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodResourceClaim {
+	    name: string;
+	    resourceClaimName?: string;
+	    resourceClaimTemplateName?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodResourceClaim(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.resourceClaimName = source["resourceClaimName"];
+	        this.resourceClaimTemplateName = source["resourceClaimTemplateName"];
+	    }
+	}
+	export class PodSchedulingGate {
+	    name: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodSchedulingGate(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	    }
+	}
+	export class PodOS {
+	    name: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodOS(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	    }
+	}
+	export class TopologySpreadConstraint {
+	    maxSkew: number;
+	    topologyKey: string;
+	    whenUnsatisfiable: string;
+	    labelSelector?: LabelSelector;
+	    minDomains?: number;
+	    nodeAffinityPolicy?: string;
+	    nodeTaintsPolicy?: string;
+	    matchLabelKeys?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new TopologySpreadConstraint(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.maxSkew = source["maxSkew"];
+	        this.topologyKey = source["topologyKey"];
+	        this.whenUnsatisfiable = source["whenUnsatisfiable"];
+	        this.labelSelector = this.convertValues(source["labelSelector"], LabelSelector);
+	        this.minDomains = source["minDomains"];
+	        this.nodeAffinityPolicy = source["nodeAffinityPolicy"];
+	        this.nodeTaintsPolicy = source["nodeTaintsPolicy"];
+	        this.matchLabelKeys = source["matchLabelKeys"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodReadinessGate {
+	    conditionType: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodReadinessGate(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.conditionType = source["conditionType"];
+	    }
+	}
+	export class PodDNSConfigOption {
+	    name?: string;
+	    value?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodDNSConfigOption(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.value = source["value"];
+	    }
+	}
+	export class PodDNSConfig {
+	    nameservers?: string[];
+	    searches?: string[];
+	    options?: PodDNSConfigOption[];
+	
+	    static createFrom(source: any = {}) {
+	        return new PodDNSConfig(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.nameservers = source["nameservers"];
+	        this.searches = source["searches"];
+	        this.options = this.convertValues(source["options"], PodDNSConfigOption);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class HostAlias {
+	    ip: string;
+	    hostnames?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new HostAlias(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.ip = source["ip"];
+	        this.hostnames = source["hostnames"];
+	    }
+	}
+	export class Toleration {
+	    key?: string;
+	    operator?: string;
+	    value?: string;
+	    effect?: string;
+	    tolerationSeconds?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new Toleration(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.key = source["key"];
+	        this.operator = source["operator"];
+	        this.value = source["value"];
+	        this.effect = source["effect"];
+	        this.tolerationSeconds = source["tolerationSeconds"];
+	    }
+	}
+	export class Sysctl {
+	    name: string;
+	    value: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new Sysctl(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.value = source["value"];
+	    }
+	}
+	export class PodSecurityContext {
+	    seLinuxOptions?: SELinuxOptions;
+	    windowsOptions?: WindowsSecurityContextOptions;
+	    runAsUser?: number;
+	    runAsGroup?: number;
+	    runAsNonRoot?: boolean;
+	    supplementalGroups?: number[];
+	    supplementalGroupsPolicy?: string;
+	    fsGroup?: number;
+	    sysctls?: Sysctl[];
+	    fsGroupChangePolicy?: string;
+	    seccompProfile?: SeccompProfile;
+	    appArmorProfile?: AppArmorProfile;
+	    seLinuxChangePolicy?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodSecurityContext(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.seLinuxOptions = this.convertValues(source["seLinuxOptions"], SELinuxOptions);
+	        this.windowsOptions = this.convertValues(source["windowsOptions"], WindowsSecurityContextOptions);
+	        this.runAsUser = source["runAsUser"];
+	        this.runAsGroup = source["runAsGroup"];
+	        this.runAsNonRoot = source["runAsNonRoot"];
+	        this.supplementalGroups = source["supplementalGroups"];
+	        this.supplementalGroupsPolicy = source["supplementalGroupsPolicy"];
+	        this.fsGroup = source["fsGroup"];
+	        this.sysctls = this.convertValues(source["sysctls"], Sysctl);
+	        this.fsGroupChangePolicy = source["fsGroupChangePolicy"];
+	        this.seccompProfile = this.convertValues(source["seccompProfile"], SeccompProfile);
+	        this.appArmorProfile = this.convertValues(source["appArmorProfile"], AppArmorProfile);
+	        this.seLinuxChangePolicy = source["seLinuxChangePolicy"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class EphemeralContainer {
+	    name: string;
+	    image?: string;
+	    command?: string[];
+	    args?: string[];
+	    workingDir?: string;
+	    ports?: ContainerPort[];
+	    envFrom?: EnvFromSource[];
+	    env?: EnvVar[];
+	    resources?: ResourceRequirements;
+	    resizePolicy?: ContainerResizePolicy[];
+	    restartPolicy?: string;
+	    volumeMounts?: VolumeMount[];
+	    volumeDevices?: VolumeDevice[];
+	    livenessProbe?: Probe;
+	    readinessProbe?: Probe;
+	    startupProbe?: Probe;
+	    lifecycle?: Lifecycle;
+	    terminationMessagePath?: string;
+	    terminationMessagePolicy?: string;
+	    imagePullPolicy?: string;
+	    securityContext?: SecurityContext;
+	    stdin?: boolean;
+	    stdinOnce?: boolean;
+	    tty?: boolean;
+	    targetContainerName?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new EphemeralContainer(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.image = source["image"];
+	        this.command = source["command"];
+	        this.args = source["args"];
+	        this.workingDir = source["workingDir"];
+	        this.ports = this.convertValues(source["ports"], ContainerPort);
+	        this.envFrom = this.convertValues(source["envFrom"], EnvFromSource);
+	        this.env = this.convertValues(source["env"], EnvVar);
+	        this.resources = this.convertValues(source["resources"], ResourceRequirements);
+	        this.resizePolicy = this.convertValues(source["resizePolicy"], ContainerResizePolicy);
+	        this.restartPolicy = source["restartPolicy"];
+	        this.volumeMounts = this.convertValues(source["volumeMounts"], VolumeMount);
+	        this.volumeDevices = this.convertValues(source["volumeDevices"], VolumeDevice);
+	        this.livenessProbe = this.convertValues(source["livenessProbe"], Probe);
+	        this.readinessProbe = this.convertValues(source["readinessProbe"], Probe);
+	        this.startupProbe = this.convertValues(source["startupProbe"], Probe);
+	        this.lifecycle = this.convertValues(source["lifecycle"], Lifecycle);
+	        this.terminationMessagePath = source["terminationMessagePath"];
+	        this.terminationMessagePolicy = source["terminationMessagePolicy"];
+	        this.imagePullPolicy = source["imagePullPolicy"];
+	        this.securityContext = this.convertValues(source["securityContext"], SecurityContext);
+	        this.stdin = source["stdin"];
+	        this.stdinOnce = source["stdinOnce"];
+	        this.tty = source["tty"];
+	        this.targetContainerName = source["targetContainerName"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ImageVolumeSource {
+	    reference?: string;
+	    pullPolicy?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ImageVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.reference = source["reference"];
+	        this.pullPolicy = source["pullPolicy"];
+	    }
+	}
+	export class TypedObjectReference {
+	    apiGroup?: string;
+	    kind: string;
+	    name: string;
+	    namespace?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new TypedObjectReference(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.apiGroup = source["apiGroup"];
+	        this.kind = source["kind"];
+	        this.name = source["name"];
+	        this.namespace = source["namespace"];
+	    }
+	}
+	export class TypedLocalObjectReference {
+	    apiGroup?: string;
+	    kind: string;
+	    name: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new TypedLocalObjectReference(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.apiGroup = source["apiGroup"];
+	        this.kind = source["kind"];
+	        this.name = source["name"];
+	    }
+	}
+	export class VolumeResourceRequirements {
+	    limits?: Record<string, resource.Quantity>;
+	    requests?: Record<string, resource.Quantity>;
+	
+	    static createFrom(source: any = {}) {
+	        return new VolumeResourceRequirements(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.limits = this.convertValues(source["limits"], resource.Quantity, true);
+	        this.requests = this.convertValues(source["requests"], resource.Quantity, true);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumeClaimSpec {
+	    accessModes?: string[];
+	    selector?: LabelSelector;
+	    resources?: VolumeResourceRequirements;
+	    volumeName?: string;
+	    storageClassName?: string;
+	    volumeMode?: string;
+	    dataSource?: TypedLocalObjectReference;
+	    dataSourceRef?: TypedObjectReference;
+	    volumeAttributesClassName?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeClaimSpec(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.accessModes = source["accessModes"];
+	        this.selector = this.convertValues(source["selector"], LabelSelector);
+	        this.resources = this.convertValues(source["resources"], VolumeResourceRequirements);
+	        this.volumeName = source["volumeName"];
+	        this.storageClassName = source["storageClassName"];
+	        this.volumeMode = source["volumeMode"];
+	        this.dataSource = this.convertValues(source["dataSource"], TypedLocalObjectReference);
+	        this.dataSourceRef = this.convertValues(source["dataSourceRef"], TypedObjectReference);
+	        this.volumeAttributesClassName = source["volumeAttributesClassName"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumeClaimTemplate {
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec: PersistentVolumeClaimSpec;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeClaimTemplate(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], PersistentVolumeClaimSpec);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class EphemeralVolumeSource {
+	    // Go type: PersistentVolumeClaimTemplate
+	    volumeClaimTemplate?: any;
+	
+	    static createFrom(source: any = {}) {
+	        return new EphemeralVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumeClaimTemplate = this.convertValues(source["volumeClaimTemplate"], null);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class CSIVolumeSource {
+	    driver: string;
+	    readOnly?: boolean;
+	    fsType?: string;
+	    volumeAttributes?: Record<string, string>;
+	    nodePublishSecretRef?: LocalObjectReference;
+	
+	    static createFrom(source: any = {}) {
+	        return new CSIVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.driver = source["driver"];
+	        this.readOnly = source["readOnly"];
+	        this.fsType = source["fsType"];
+	        this.volumeAttributes = source["volumeAttributes"];
+	        this.nodePublishSecretRef = this.convertValues(source["nodePublishSecretRef"], LocalObjectReference);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class StorageOSVolumeSource {
+	    volumeName?: string;
+	    volumeNamespace?: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	    secretRef?: LocalObjectReference;
+	
+	    static createFrom(source: any = {}) {
+	        return new StorageOSVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumeName = source["volumeName"];
+	        this.volumeNamespace = source["volumeNamespace"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	        this.secretRef = this.convertValues(source["secretRef"], LocalObjectReference);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ScaleIOVolumeSource {
+	    gateway: string;
+	    system: string;
+	    secretRef?: LocalObjectReference;
+	    sslEnabled?: boolean;
+	    protectionDomain?: string;
+	    storagePool?: string;
+	    storageMode?: string;
+	    volumeName?: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new ScaleIOVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.gateway = source["gateway"];
+	        this.system = source["system"];
+	        this.secretRef = this.convertValues(source["secretRef"], LocalObjectReference);
+	        this.sslEnabled = source["sslEnabled"];
+	        this.protectionDomain = source["protectionDomain"];
+	        this.storagePool = source["storagePool"];
+	        this.storageMode = source["storageMode"];
+	        this.volumeName = source["volumeName"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PortworxVolumeSource {
+	    volumeID: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new PortworxVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumeID = source["volumeID"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	    }
+	}
+	export class ClusterTrustBundleProjection {
+	    name?: string;
+	    signerName?: string;
+	    labelSelector?: LabelSelector;
+	    optional?: boolean;
+	    path: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ClusterTrustBundleProjection(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.signerName = source["signerName"];
+	        this.labelSelector = this.convertValues(source["labelSelector"], LabelSelector);
+	        this.optional = source["optional"];
+	        this.path = source["path"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ServiceAccountTokenProjection {
+	    audience?: string;
+	    expirationSeconds?: number;
+	    path: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ServiceAccountTokenProjection(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.audience = source["audience"];
+	        this.expirationSeconds = source["expirationSeconds"];
+	        this.path = source["path"];
+	    }
+	}
+	export class ConfigMapProjection {
+	    name?: string;
+	    items?: KeyToPath[];
+	    optional?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new ConfigMapProjection(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.items = this.convertValues(source["items"], KeyToPath);
+	        this.optional = source["optional"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class DownwardAPIProjection {
+	    items?: DownwardAPIVolumeFile[];
+	
+	    static createFrom(source: any = {}) {
+	        return new DownwardAPIProjection(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.items = this.convertValues(source["items"], DownwardAPIVolumeFile);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class SecretProjection {
+	    name?: string;
+	    items?: KeyToPath[];
+	    optional?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new SecretProjection(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.items = this.convertValues(source["items"], KeyToPath);
+	        this.optional = source["optional"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class VolumeProjection {
+	    // Go type: SecretProjection
+	    secret?: any;
+	    // Go type: DownwardAPIProjection
+	    downwardAPI?: any;
+	    // Go type: ConfigMapProjection
+	    configMap?: any;
+	    // Go type: ServiceAccountTokenProjection
+	    serviceAccountToken?: any;
+	    // Go type: ClusterTrustBundleProjection
+	    clusterTrustBundle?: any;
+	
+	    static createFrom(source: any = {}) {
+	        return new VolumeProjection(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.secret = this.convertValues(source["secret"], null);
+	        this.downwardAPI = this.convertValues(source["downwardAPI"], null);
+	        this.configMap = this.convertValues(source["configMap"], null);
+	        this.serviceAccountToken = this.convertValues(source["serviceAccountToken"], null);
+	        this.clusterTrustBundle = this.convertValues(source["clusterTrustBundle"], null);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ProjectedVolumeSource {
+	    sources: VolumeProjection[];
+	    defaultMode?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new ProjectedVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.sources = this.convertValues(source["sources"], VolumeProjection);
+	        this.defaultMode = source["defaultMode"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PhotonPersistentDiskVolumeSource {
+	    pdID: string;
+	    fsType?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PhotonPersistentDiskVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.pdID = source["pdID"];
+	        this.fsType = source["fsType"];
+	    }
+	}
+	export class AzureDiskVolumeSource {
+	    diskName: string;
+	    diskURI: string;
+	    cachingMode?: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	    kind?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new AzureDiskVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.diskName = source["diskName"];
+	        this.diskURI = source["diskURI"];
+	        this.cachingMode = source["cachingMode"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	        this.kind = source["kind"];
+	    }
+	}
+	export class QuobyteVolumeSource {
+	    registry: string;
+	    volume: string;
+	    readOnly?: boolean;
+	    user?: string;
+	    group?: string;
+	    tenant?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new QuobyteVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.registry = source["registry"];
+	        this.volume = source["volume"];
+	        this.readOnly = source["readOnly"];
+	        this.user = source["user"];
+	        this.group = source["group"];
+	        this.tenant = source["tenant"];
+	    }
+	}
+	export class VsphereVirtualDiskVolumeSource {
+	    volumePath: string;
+	    fsType?: string;
+	    storagePolicyName?: string;
+	    storagePolicyID?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new VsphereVirtualDiskVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumePath = source["volumePath"];
+	        this.fsType = source["fsType"];
+	        this.storagePolicyName = source["storagePolicyName"];
+	        this.storagePolicyID = source["storagePolicyID"];
+	    }
+	}
+	export class ConfigMapVolumeSource {
+	    name?: string;
+	    items?: KeyToPath[];
+	    defaultMode?: number;
+	    optional?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new ConfigMapVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.items = this.convertValues(source["items"], KeyToPath);
+	        this.defaultMode = source["defaultMode"];
+	        this.optional = source["optional"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class AzureFileVolumeSource {
+	    secretName: string;
+	    shareName: string;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new AzureFileVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.secretName = source["secretName"];
+	        this.shareName = source["shareName"];
+	        this.readOnly = source["readOnly"];
+	    }
+	}
+	export class FCVolumeSource {
+	    targetWWNs?: string[];
+	    lun?: number;
+	    fsType?: string;
+	    readOnly?: boolean;
+	    wwids?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new FCVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.targetWWNs = source["targetWWNs"];
+	        this.lun = source["lun"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	        this.wwids = source["wwids"];
+	    }
+	}
+	export class DownwardAPIVolumeFile {
+	    path: string;
+	    fieldRef?: ObjectFieldSelector;
+	    resourceFieldRef?: ResourceFieldSelector;
+	    mode?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new DownwardAPIVolumeFile(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.path = source["path"];
+	        this.fieldRef = this.convertValues(source["fieldRef"], ObjectFieldSelector);
+	        this.resourceFieldRef = this.convertValues(source["resourceFieldRef"], ResourceFieldSelector);
+	        this.mode = source["mode"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class DownwardAPIVolumeSource {
+	    items?: DownwardAPIVolumeFile[];
+	    defaultMode?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new DownwardAPIVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.items = this.convertValues(source["items"], DownwardAPIVolumeFile);
+	        this.defaultMode = source["defaultMode"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class FlockerVolumeSource {
+	    datasetName?: string;
+	    datasetUUID?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new FlockerVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.datasetName = source["datasetName"];
+	        this.datasetUUID = source["datasetUUID"];
+	    }
+	}
+	export class CephFSVolumeSource {
+	    monitors: string[];
+	    path?: string;
+	    user?: string;
+	    secretFile?: string;
+	    secretRef?: LocalObjectReference;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new CephFSVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.monitors = source["monitors"];
+	        this.path = source["path"];
+	        this.user = source["user"];
+	        this.secretFile = source["secretFile"];
+	        this.secretRef = this.convertValues(source["secretRef"], LocalObjectReference);
+	        this.readOnly = source["readOnly"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class CinderVolumeSource {
+	    volumeID: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	    secretRef?: LocalObjectReference;
+	
+	    static createFrom(source: any = {}) {
+	        return new CinderVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumeID = source["volumeID"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	        this.secretRef = this.convertValues(source["secretRef"], LocalObjectReference);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class FlexVolumeSource {
+	    driver: string;
+	    fsType?: string;
+	    secretRef?: LocalObjectReference;
+	    readOnly?: boolean;
+	    options?: Record<string, string>;
+	
+	    static createFrom(source: any = {}) {
+	        return new FlexVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.driver = source["driver"];
+	        this.fsType = source["fsType"];
+	        this.secretRef = this.convertValues(source["secretRef"], LocalObjectReference);
+	        this.readOnly = source["readOnly"];
+	        this.options = source["options"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class RBDVolumeSource {
+	    monitors: string[];
+	    image: string;
+	    fsType?: string;
+	    pool?: string;
+	    user?: string;
+	    keyring?: string;
+	    secretRef?: LocalObjectReference;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new RBDVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.monitors = source["monitors"];
+	        this.image = source["image"];
+	        this.fsType = source["fsType"];
+	        this.pool = source["pool"];
+	        this.user = source["user"];
+	        this.keyring = source["keyring"];
+	        this.secretRef = this.convertValues(source["secretRef"], LocalObjectReference);
+	        this.readOnly = source["readOnly"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumeClaimVolumeSource {
+	    claimName: string;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeClaimVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.claimName = source["claimName"];
+	        this.readOnly = source["readOnly"];
+	    }
+	}
+	export class GlusterfsVolumeSource {
+	    endpoints: string;
+	    path: string;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new GlusterfsVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.endpoints = source["endpoints"];
+	        this.path = source["path"];
+	        this.readOnly = source["readOnly"];
+	    }
+	}
+	export class LocalObjectReference {
+	    name?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new LocalObjectReference(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	    }
+	}
+	export class ISCSIVolumeSource {
+	    targetPortal: string;
+	    iqn: string;
+	    lun: number;
+	    iscsiInterface?: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	    portals?: string[];
+	    chapAuthDiscovery?: boolean;
+	    chapAuthSession?: boolean;
+	    secretRef?: LocalObjectReference;
+	    initiatorName?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ISCSIVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.targetPortal = source["targetPortal"];
+	        this.iqn = source["iqn"];
+	        this.lun = source["lun"];
+	        this.iscsiInterface = source["iscsiInterface"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	        this.portals = source["portals"];
+	        this.chapAuthDiscovery = source["chapAuthDiscovery"];
+	        this.chapAuthSession = source["chapAuthSession"];
+	        this.secretRef = this.convertValues(source["secretRef"], LocalObjectReference);
+	        this.initiatorName = source["initiatorName"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class NFSVolumeSource {
+	    server: string;
+	    path: string;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new NFSVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.server = source["server"];
+	        this.path = source["path"];
+	        this.readOnly = source["readOnly"];
+	    }
+	}
+	export class KeyToPath {
+	    key: string;
+	    path: string;
+	    mode?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new KeyToPath(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.key = source["key"];
+	        this.path = source["path"];
+	        this.mode = source["mode"];
+	    }
+	}
+	export class SecretVolumeSource {
+	    secretName?: string;
+	    items?: KeyToPath[];
+	    defaultMode?: number;
+	    optional?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new SecretVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.secretName = source["secretName"];
+	        this.items = this.convertValues(source["items"], KeyToPath);
+	        this.defaultMode = source["defaultMode"];
+	        this.optional = source["optional"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class GitRepoVolumeSource {
+	    repository: string;
+	    revision?: string;
+	    directory?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new GitRepoVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.repository = source["repository"];
+	        this.revision = source["revision"];
+	        this.directory = source["directory"];
+	    }
+	}
+	export class AWSElasticBlockStoreVolumeSource {
+	    volumeID: string;
+	    fsType?: string;
+	    partition?: number;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new AWSElasticBlockStoreVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumeID = source["volumeID"];
+	        this.fsType = source["fsType"];
+	        this.partition = source["partition"];
+	        this.readOnly = source["readOnly"];
+	    }
+	}
+	export class GCEPersistentDiskVolumeSource {
+	    pdName: string;
+	    fsType?: string;
+	    partition?: number;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new GCEPersistentDiskVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.pdName = source["pdName"];
+	        this.fsType = source["fsType"];
+	        this.partition = source["partition"];
+	        this.readOnly = source["readOnly"];
+	    }
+	}
+	export class EmptyDirVolumeSource {
+	    medium?: string;
+	    sizeLimit?: resource.Quantity;
+	
+	    static createFrom(source: any = {}) {
+	        return new EmptyDirVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.medium = source["medium"];
+	        this.sizeLimit = this.convertValues(source["sizeLimit"], resource.Quantity);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class HostPathVolumeSource {
+	    path: string;
+	    type?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new HostPathVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.path = source["path"];
+	        this.type = source["type"];
+	    }
+	}
+	export class Volume {
+	    name: string;
+	    // Go type: HostPathVolumeSource
+	    hostPath?: any;
+	    // Go type: EmptyDirVolumeSource
+	    emptyDir?: any;
+	    // Go type: GCEPersistentDiskVolumeSource
+	    gcePersistentDisk?: any;
+	    // Go type: AWSElasticBlockStoreVolumeSource
+	    awsElasticBlockStore?: any;
+	    // Go type: GitRepoVolumeSource
+	    gitRepo?: any;
+	    // Go type: SecretVolumeSource
+	    secret?: any;
+	    // Go type: NFSVolumeSource
+	    nfs?: any;
+	    // Go type: ISCSIVolumeSource
+	    iscsi?: any;
+	    // Go type: GlusterfsVolumeSource
+	    glusterfs?: any;
+	    // Go type: PersistentVolumeClaimVolumeSource
+	    persistentVolumeClaim?: any;
+	    // Go type: RBDVolumeSource
+	    rbd?: any;
+	    // Go type: FlexVolumeSource
+	    flexVolume?: any;
+	    // Go type: CinderVolumeSource
+	    cinder?: any;
+	    // Go type: CephFSVolumeSource
+	    cephfs?: any;
+	    // Go type: FlockerVolumeSource
+	    flocker?: any;
+	    // Go type: DownwardAPIVolumeSource
+	    downwardAPI?: any;
+	    // Go type: FCVolumeSource
+	    fc?: any;
+	    // Go type: AzureFileVolumeSource
+	    azureFile?: any;
+	    // Go type: ConfigMapVolumeSource
+	    configMap?: any;
+	    // Go type: VsphereVirtualDiskVolumeSource
+	    vsphereVolume?: any;
+	    // Go type: QuobyteVolumeSource
+	    quobyte?: any;
+	    // Go type: AzureDiskVolumeSource
+	    azureDisk?: any;
+	    // Go type: PhotonPersistentDiskVolumeSource
+	    photonPersistentDisk?: any;
+	    // Go type: ProjectedVolumeSource
+	    projected?: any;
+	    // Go type: PortworxVolumeSource
+	    portworxVolume?: any;
+	    // Go type: ScaleIOVolumeSource
+	    scaleIO?: any;
+	    // Go type: StorageOSVolumeSource
+	    storageos?: any;
+	    // Go type: CSIVolumeSource
+	    csi?: any;
+	    // Go type: EphemeralVolumeSource
+	    ephemeral?: any;
+	    // Go type: ImageVolumeSource
+	    image?: any;
+	
+	    static createFrom(source: any = {}) {
+	        return new Volume(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.hostPath = this.convertValues(source["hostPath"], null);
+	        this.emptyDir = this.convertValues(source["emptyDir"], null);
+	        this.gcePersistentDisk = this.convertValues(source["gcePersistentDisk"], null);
+	        this.awsElasticBlockStore = this.convertValues(source["awsElasticBlockStore"], null);
+	        this.gitRepo = this.convertValues(source["gitRepo"], null);
+	        this.secret = this.convertValues(source["secret"], null);
+	        this.nfs = this.convertValues(source["nfs"], null);
+	        this.iscsi = this.convertValues(source["iscsi"], null);
+	        this.glusterfs = this.convertValues(source["glusterfs"], null);
+	        this.persistentVolumeClaim = this.convertValues(source["persistentVolumeClaim"], null);
+	        this.rbd = this.convertValues(source["rbd"], null);
+	        this.flexVolume = this.convertValues(source["flexVolume"], null);
+	        this.cinder = this.convertValues(source["cinder"], null);
+	        this.cephfs = this.convertValues(source["cephfs"], null);
+	        this.flocker = this.convertValues(source["flocker"], null);
+	        this.downwardAPI = this.convertValues(source["downwardAPI"], null);
+	        this.fc = this.convertValues(source["fc"], null);
+	        this.azureFile = this.convertValues(source["azureFile"], null);
+	        this.configMap = this.convertValues(source["configMap"], null);
+	        this.vsphereVolume = this.convertValues(source["vsphereVolume"], null);
+	        this.quobyte = this.convertValues(source["quobyte"], null);
+	        this.azureDisk = this.convertValues(source["azureDisk"], null);
+	        this.photonPersistentDisk = this.convertValues(source["photonPersistentDisk"], null);
+	        this.projected = this.convertValues(source["projected"], null);
+	        this.portworxVolume = this.convertValues(source["portworxVolume"], null);
+	        this.scaleIO = this.convertValues(source["scaleIO"], null);
+	        this.storageos = this.convertValues(source["storageos"], null);
+	        this.csi = this.convertValues(source["csi"], null);
+	        this.ephemeral = this.convertValues(source["ephemeral"], null);
+	        this.image = this.convertValues(source["image"], null);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodSpec {
+	    volumes?: Volume[];
+	    initContainers?: Container[];
+	    containers: Container[];
+	    ephemeralContainers?: EphemeralContainer[];
+	    restartPolicy?: string;
+	    terminationGracePeriodSeconds?: number;
+	    activeDeadlineSeconds?: number;
+	    dnsPolicy?: string;
+	    nodeSelector?: Record<string, string>;
+	    serviceAccountName?: string;
+	    serviceAccount?: string;
+	    automountServiceAccountToken?: boolean;
+	    nodeName?: string;
+	    hostNetwork?: boolean;
+	    hostPID?: boolean;
+	    hostIPC?: boolean;
+	    shareProcessNamespace?: boolean;
+	    securityContext?: PodSecurityContext;
+	    imagePullSecrets?: LocalObjectReference[];
+	    hostname?: string;
+	    subdomain?: string;
+	    affinity?: Affinity;
+	    schedulerName?: string;
+	    tolerations?: Toleration[];
+	    hostAliases?: HostAlias[];
+	    priorityClassName?: string;
+	    priority?: number;
+	    dnsConfig?: PodDNSConfig;
+	    readinessGates?: PodReadinessGate[];
+	    runtimeClassName?: string;
+	    enableServiceLinks?: boolean;
+	    preemptionPolicy?: string;
+	    overhead?: Record<string, resource.Quantity>;
+	    topologySpreadConstraints?: TopologySpreadConstraint[];
+	    setHostnameAsFQDN?: boolean;
+	    os?: PodOS;
+	    hostUsers?: boolean;
+	    schedulingGates?: PodSchedulingGate[];
+	    resourceClaims?: PodResourceClaim[];
+	    resources?: ResourceRequirements;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodSpec(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumes = this.convertValues(source["volumes"], Volume);
+	        this.initContainers = this.convertValues(source["initContainers"], Container);
+	        this.containers = this.convertValues(source["containers"], Container);
+	        this.ephemeralContainers = this.convertValues(source["ephemeralContainers"], EphemeralContainer);
+	        this.restartPolicy = source["restartPolicy"];
+	        this.terminationGracePeriodSeconds = source["terminationGracePeriodSeconds"];
+	        this.activeDeadlineSeconds = source["activeDeadlineSeconds"];
+	        this.dnsPolicy = source["dnsPolicy"];
+	        this.nodeSelector = source["nodeSelector"];
+	        this.serviceAccountName = source["serviceAccountName"];
+	        this.serviceAccount = source["serviceAccount"];
+	        this.automountServiceAccountToken = source["automountServiceAccountToken"];
+	        this.nodeName = source["nodeName"];
+	        this.hostNetwork = source["hostNetwork"];
+	        this.hostPID = source["hostPID"];
+	        this.hostIPC = source["hostIPC"];
+	        this.shareProcessNamespace = source["shareProcessNamespace"];
+	        this.securityContext = this.convertValues(source["securityContext"], PodSecurityContext);
+	        this.imagePullSecrets = this.convertValues(source["imagePullSecrets"], LocalObjectReference);
+	        this.hostname = source["hostname"];
+	        this.subdomain = source["subdomain"];
+	        this.affinity = this.convertValues(source["affinity"], Affinity);
+	        this.schedulerName = source["schedulerName"];
+	        this.tolerations = this.convertValues(source["tolerations"], Toleration);
+	        this.hostAliases = this.convertValues(source["hostAliases"], HostAlias);
+	        this.priorityClassName = source["priorityClassName"];
+	        this.priority = source["priority"];
+	        this.dnsConfig = this.convertValues(source["dnsConfig"], PodDNSConfig);
+	        this.readinessGates = this.convertValues(source["readinessGates"], PodReadinessGate);
+	        this.runtimeClassName = source["runtimeClassName"];
+	        this.enableServiceLinks = source["enableServiceLinks"];
+	        this.preemptionPolicy = source["preemptionPolicy"];
+	        this.overhead = this.convertValues(source["overhead"], resource.Quantity, true);
+	        this.topologySpreadConstraints = this.convertValues(source["topologySpreadConstraints"], TopologySpreadConstraint);
+	        this.setHostnameAsFQDN = source["setHostnameAsFQDN"];
+	        this.os = this.convertValues(source["os"], PodOS);
+	        this.hostUsers = source["hostUsers"];
+	        this.schedulingGates = this.convertValues(source["schedulingGates"], PodSchedulingGate);
+	        this.resourceClaims = this.convertValues(source["resourceClaims"], PodResourceClaim);
+	        this.resources = this.convertValues(source["resources"], ResourceRequirements);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodTemplateSpec {
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec?: PodSpec;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodTemplateSpec(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], PodSpec);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class DeploymentSpec {
+	    replicas?: number;
+	    selector?: LabelSelector;
+	    template: PodTemplateSpec;
+	    strategy?: DeploymentStrategy;
+	    minReadySeconds?: number;
+	    revisionHistoryLimit?: number;
+	    paused?: boolean;
+	    progressDeadlineSeconds?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new DeploymentSpec(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.replicas = source["replicas"];
+	        this.selector = this.convertValues(source["selector"], LabelSelector);
+	        this.template = this.convertValues(source["template"], PodTemplateSpec);
+	        this.strategy = this.convertValues(source["strategy"], DeploymentStrategy);
+	        this.minReadySeconds = source["minReadySeconds"];
+	        this.revisionHistoryLimit = source["revisionHistoryLimit"];
+	        this.paused = source["paused"];
+	        this.progressDeadlineSeconds = source["progressDeadlineSeconds"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class Deployment {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec?: DeploymentSpec;
+	    status?: DeploymentStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new Deployment(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], DeploymentSpec);
+	        this.status = this.convertValues(source["status"], DeploymentStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	export class HostIP {
+	    ip: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new HostIP(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.ip = source["ip"];
+	    }
+	}
+	export class IngressPortStatus {
+	    port: number;
+	    protocol: string;
+	    error?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressPortStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.port = source["port"];
+	        this.protocol = source["protocol"];
+	        this.error = source["error"];
+	    }
+	}
+	export class IngressLoadBalancerIngress {
+	    ip?: string;
+	    hostname?: string;
+	    ports?: IngressPortStatus[];
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressLoadBalancerIngress(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.ip = source["ip"];
+	        this.hostname = source["hostname"];
+	        this.ports = this.convertValues(source["ports"], IngressPortStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class IngressLoadBalancerStatus {
+	    ingress?: IngressLoadBalancerIngress[];
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressLoadBalancerStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.ingress = this.convertValues(source["ingress"], IngressLoadBalancerIngress);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class IngressStatus {
+	    loadBalancer?: IngressLoadBalancerStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.loadBalancer = this.convertValues(source["loadBalancer"], IngressLoadBalancerStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class HTTPIngressPath {
+	    path?: string;
+	    pathType?: string;
+	    backend: IngressBackend;
+	
+	    static createFrom(source: any = {}) {
+	        return new HTTPIngressPath(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.path = source["path"];
+	        this.pathType = source["pathType"];
+	        this.backend = this.convertValues(source["backend"], IngressBackend);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class HTTPIngressRuleValue {
+	    paths: HTTPIngressPath[];
+	
+	    static createFrom(source: any = {}) {
+	        return new HTTPIngressRuleValue(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.paths = this.convertValues(source["paths"], HTTPIngressPath);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class IngressRule {
+	    host?: string;
+	    // Go type: HTTPIngressRuleValue
+	    http?: any;
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressRule(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.host = source["host"];
+	        this.http = this.convertValues(source["http"], null);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class IngressTLS {
+	    hosts?: string[];
+	    secretName?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressTLS(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.hosts = source["hosts"];
+	        this.secretName = source["secretName"];
+	    }
+	}
+	export class ServiceBackendPort {
+	    name?: string;
+	    number?: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new ServiceBackendPort(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.number = source["number"];
+	    }
+	}
+	export class IngressServiceBackend {
+	    name: string;
+	    port?: ServiceBackendPort;
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressServiceBackend(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.port = this.convertValues(source["port"], ServiceBackendPort);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class IngressBackend {
+	    service?: IngressServiceBackend;
+	    resource?: TypedLocalObjectReference;
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressBackend(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.service = this.convertValues(source["service"], IngressServiceBackend);
+	        this.resource = this.convertValues(source["resource"], TypedLocalObjectReference);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class IngressSpec {
+	    ingressClassName?: string;
+	    defaultBackend?: IngressBackend;
+	    tls?: IngressTLS[];
+	    rules?: IngressRule[];
+	
+	    static createFrom(source: any = {}) {
+	        return new IngressSpec(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.ingressClassName = source["ingressClassName"];
+	        this.defaultBackend = this.convertValues(source["defaultBackend"], IngressBackend);
+	        this.tls = this.convertValues(source["tls"], IngressTLS);
+	        this.rules = this.convertValues(source["rules"], IngressRule);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class Ingress {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec?: IngressSpec;
+	    status?: IngressStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new Ingress(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], IngressSpec);
+	        this.status = this.convertValues(source["status"], IngressStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	export class ModifyVolumeStatus {
+	    targetVolumeAttributesClassName?: string;
+	    status: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ModifyVolumeStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.targetVolumeAttributesClassName = source["targetVolumeAttributesClassName"];
+	        this.status = source["status"];
+	    }
+	}
+	export class NamespaceCondition {
+	    type: string;
+	    status: string;
+	    lastTransitionTime?: Time;
+	    reason?: string;
+	    message?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new NamespaceCondition(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.status = source["status"];
+	        this.lastTransitionTime = this.convertValues(source["lastTransitionTime"], Time);
+	        this.reason = source["reason"];
+	        this.message = source["message"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class NamespaceStatus {
+	    phase?: string;
+	    conditions?: NamespaceCondition[];
+	
+	    static createFrom(source: any = {}) {
+	        return new NamespaceStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.phase = source["phase"];
+	        this.conditions = this.convertValues(source["conditions"], NamespaceCondition);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class NamespaceSpec {
+	    finalizers?: string[];
+	
+	    static createFrom(source: any = {}) {
+	        return new NamespaceSpec(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.finalizers = source["finalizers"];
+	    }
+	}
+	export class Namespace {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec?: NamespaceSpec;
+	    status?: NamespaceStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new Namespace(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], NamespaceSpec);
+	        this.status = this.convertValues(source["status"], NamespaceStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+	
+	
+	
+	
+	
+	export class ObjectReference {
+	    kind?: string;
+	    namespace?: string;
+	    name?: string;
+	    uid?: string;
+	    apiVersion?: string;
+	    resourceVersion?: string;
+	    fieldPath?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ObjectReference(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.namespace = source["namespace"];
+	        this.name = source["name"];
+	        this.uid = source["uid"];
+	        this.apiVersion = source["apiVersion"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.fieldPath = source["fieldPath"];
+	    }
+	}
+	export class PersistentVolumeStatus {
+	    phase?: string;
+	    message?: string;
+	    reason?: string;
+	    lastPhaseTransitionTime?: Time;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.phase = source["phase"];
+	        this.message = source["message"];
+	        this.reason = source["reason"];
+	        this.lastPhaseTransitionTime = this.convertValues(source["lastPhaseTransitionTime"], Time);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class VolumeNodeAffinity {
+	    required?: NodeSelector;
+	
+	    static createFrom(source: any = {}) {
+	        return new VolumeNodeAffinity(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.required = this.convertValues(source["required"], NodeSelector);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class CSIPersistentVolumeSource {
+	    driver: string;
+	    volumeHandle: string;
+	    readOnly?: boolean;
+	    fsType?: string;
+	    volumeAttributes?: Record<string, string>;
+	    // Go type: SecretReference
+	    controllerPublishSecretRef?: any;
+	    // Go type: SecretReference
+	    nodeStageSecretRef?: any;
+	    // Go type: SecretReference
+	    nodePublishSecretRef?: any;
+	    // Go type: SecretReference
+	    controllerExpandSecretRef?: any;
+	    // Go type: SecretReference
+	    nodeExpandSecretRef?: any;
+	
+	    static createFrom(source: any = {}) {
+	        return new CSIPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.driver = source["driver"];
+	        this.volumeHandle = source["volumeHandle"];
+	        this.readOnly = source["readOnly"];
+	        this.fsType = source["fsType"];
+	        this.volumeAttributes = source["volumeAttributes"];
+	        this.controllerPublishSecretRef = this.convertValues(source["controllerPublishSecretRef"], null);
+	        this.nodeStageSecretRef = this.convertValues(source["nodeStageSecretRef"], null);
+	        this.nodePublishSecretRef = this.convertValues(source["nodePublishSecretRef"], null);
+	        this.controllerExpandSecretRef = this.convertValues(source["controllerExpandSecretRef"], null);
+	        this.nodeExpandSecretRef = this.convertValues(source["nodeExpandSecretRef"], null);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class StorageOSPersistentVolumeSource {
+	    volumeName?: string;
+	    volumeNamespace?: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	    secretRef?: ObjectReference;
+	
+	    static createFrom(source: any = {}) {
+	        return new StorageOSPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumeName = source["volumeName"];
+	        this.volumeNamespace = source["volumeNamespace"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	        this.secretRef = this.convertValues(source["secretRef"], ObjectReference);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class LocalVolumeSource {
+	    path: string;
+	    fsType?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new LocalVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.path = source["path"];
+	        this.fsType = source["fsType"];
+	    }
+	}
+	export class ScaleIOPersistentVolumeSource {
+	    gateway: string;
+	    system: string;
+	    // Go type: SecretReference
+	    secretRef?: any;
+	    sslEnabled?: boolean;
+	    protectionDomain?: string;
+	    storagePool?: string;
+	    storageMode?: string;
+	    volumeName?: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new ScaleIOPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.gateway = source["gateway"];
+	        this.system = source["system"];
+	        this.secretRef = this.convertValues(source["secretRef"], null);
+	        this.sslEnabled = source["sslEnabled"];
+	        this.protectionDomain = source["protectionDomain"];
+	        this.storagePool = source["storagePool"];
+	        this.storageMode = source["storageMode"];
+	        this.volumeName = source["volumeName"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class AzureFilePersistentVolumeSource {
+	    secretName: string;
+	    shareName: string;
+	    readOnly?: boolean;
+	    secretNamespace?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new AzureFilePersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.secretName = source["secretName"];
+	        this.shareName = source["shareName"];
+	        this.readOnly = source["readOnly"];
+	        this.secretNamespace = source["secretNamespace"];
+	    }
+	}
+	export class FlexPersistentVolumeSource {
+	    driver: string;
+	    fsType?: string;
+	    // Go type: SecretReference
+	    secretRef?: any;
+	    readOnly?: boolean;
+	    options?: Record<string, string>;
+	
+	    static createFrom(source: any = {}) {
+	        return new FlexPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.driver = source["driver"];
+	        this.fsType = source["fsType"];
+	        this.secretRef = this.convertValues(source["secretRef"], null);
+	        this.readOnly = source["readOnly"];
+	        this.options = source["options"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class CephFSPersistentVolumeSource {
+	    monitors: string[];
+	    path?: string;
+	    user?: string;
+	    secretFile?: string;
+	    // Go type: SecretReference
+	    secretRef?: any;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new CephFSPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.monitors = source["monitors"];
+	        this.path = source["path"];
+	        this.user = source["user"];
+	        this.secretFile = source["secretFile"];
+	        this.secretRef = this.convertValues(source["secretRef"], null);
+	        this.readOnly = source["readOnly"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class CinderPersistentVolumeSource {
+	    volumeID: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	    // Go type: SecretReference
+	    secretRef?: any;
+	
+	    static createFrom(source: any = {}) {
+	        return new CinderPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.volumeID = source["volumeID"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	        this.secretRef = this.convertValues(source["secretRef"], null);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class ISCSIPersistentVolumeSource {
+	    targetPortal: string;
+	    iqn: string;
+	    lun: number;
+	    iscsiInterface?: string;
+	    fsType?: string;
+	    readOnly?: boolean;
+	    portals?: string[];
+	    chapAuthDiscovery?: boolean;
+	    chapAuthSession?: boolean;
+	    // Go type: SecretReference
+	    secretRef?: any;
+	    initiatorName?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new ISCSIPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.targetPortal = source["targetPortal"];
+	        this.iqn = source["iqn"];
+	        this.lun = source["lun"];
+	        this.iscsiInterface = source["iscsiInterface"];
+	        this.fsType = source["fsType"];
+	        this.readOnly = source["readOnly"];
+	        this.portals = source["portals"];
+	        this.chapAuthDiscovery = source["chapAuthDiscovery"];
+	        this.chapAuthSession = source["chapAuthSession"];
+	        this.secretRef = this.convertValues(source["secretRef"], null);
+	        this.initiatorName = source["initiatorName"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class SecretReference {
+	    name?: string;
+	    namespace?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new SecretReference(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.namespace = source["namespace"];
+	    }
+	}
+	export class RBDPersistentVolumeSource {
+	    monitors: string[];
+	    image: string;
+	    fsType?: string;
+	    pool?: string;
+	    user?: string;
+	    keyring?: string;
+	    // Go type: SecretReference
+	    secretRef?: any;
+	    readOnly?: boolean;
+	
+	    static createFrom(source: any = {}) {
+	        return new RBDPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.monitors = source["monitors"];
+	        this.image = source["image"];
+	        this.fsType = source["fsType"];
+	        this.pool = source["pool"];
+	        this.user = source["user"];
+	        this.keyring = source["keyring"];
+	        this.secretRef = this.convertValues(source["secretRef"], null);
+	        this.readOnly = source["readOnly"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class GlusterfsPersistentVolumeSource {
+	    endpoints: string;
+	    path: string;
+	    readOnly?: boolean;
+	    endpointsNamespace?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new GlusterfsPersistentVolumeSource(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.endpoints = source["endpoints"];
+	        this.path = source["path"];
+	        this.readOnly = source["readOnly"];
+	        this.endpointsNamespace = source["endpointsNamespace"];
+	    }
+	}
+	export class PersistentVolumeSpec {
+	    capacity?: Record<string, resource.Quantity>;
+	    // Go type: GCEPersistentDiskVolumeSource
+	    gcePersistentDisk?: any;
+	    // Go type: AWSElasticBlockStoreVolumeSource
+	    awsElasticBlockStore?: any;
+	    // Go type: HostPathVolumeSource
+	    hostPath?: any;
+	    // Go type: GlusterfsPersistentVolumeSource
+	    glusterfs?: any;
+	    // Go type: NFSVolumeSource
+	    nfs?: any;
+	    // Go type: RBDPersistentVolumeSource
+	    rbd?: any;
+	    // Go type: ISCSIPersistentVolumeSource
+	    iscsi?: any;
+	    // Go type: CinderPersistentVolumeSource
+	    cinder?: any;
+	    // Go type: CephFSPersistentVolumeSource
+	    cephfs?: any;
+	    // Go type: FCVolumeSource
+	    fc?: any;
+	    // Go type: FlockerVolumeSource
+	    flocker?: any;
+	    // Go type: FlexPersistentVolumeSource
+	    flexVolume?: any;
+	    // Go type: AzureFilePersistentVolumeSource
+	    azureFile?: any;
+	    // Go type: VsphereVirtualDiskVolumeSource
+	    vsphereVolume?: any;
+	    // Go type: QuobyteVolumeSource
+	    quobyte?: any;
+	    // Go type: AzureDiskVolumeSource
+	    azureDisk?: any;
+	    // Go type: PhotonPersistentDiskVolumeSource
+	    photonPersistentDisk?: any;
+	    // Go type: PortworxVolumeSource
+	    portworxVolume?: any;
+	    // Go type: ScaleIOPersistentVolumeSource
+	    scaleIO?: any;
+	    // Go type: LocalVolumeSource
+	    local?: any;
+	    // Go type: StorageOSPersistentVolumeSource
+	    storageos?: any;
+	    // Go type: CSIPersistentVolumeSource
+	    csi?: any;
+	    accessModes?: string[];
+	    claimRef?: ObjectReference;
+	    persistentVolumeReclaimPolicy?: string;
+	    storageClassName?: string;
+	    mountOptions?: string[];
+	    volumeMode?: string;
+	    nodeAffinity?: VolumeNodeAffinity;
+	    volumeAttributesClassName?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeSpec(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.capacity = this.convertValues(source["capacity"], resource.Quantity, true);
+	        this.gcePersistentDisk = this.convertValues(source["gcePersistentDisk"], null);
+	        this.awsElasticBlockStore = this.convertValues(source["awsElasticBlockStore"], null);
+	        this.hostPath = this.convertValues(source["hostPath"], null);
+	        this.glusterfs = this.convertValues(source["glusterfs"], null);
+	        this.nfs = this.convertValues(source["nfs"], null);
+	        this.rbd = this.convertValues(source["rbd"], null);
+	        this.iscsi = this.convertValues(source["iscsi"], null);
+	        this.cinder = this.convertValues(source["cinder"], null);
+	        this.cephfs = this.convertValues(source["cephfs"], null);
+	        this.fc = this.convertValues(source["fc"], null);
+	        this.flocker = this.convertValues(source["flocker"], null);
+	        this.flexVolume = this.convertValues(source["flexVolume"], null);
+	        this.azureFile = this.convertValues(source["azureFile"], null);
+	        this.vsphereVolume = this.convertValues(source["vsphereVolume"], null);
+	        this.quobyte = this.convertValues(source["quobyte"], null);
+	        this.azureDisk = this.convertValues(source["azureDisk"], null);
+	        this.photonPersistentDisk = this.convertValues(source["photonPersistentDisk"], null);
+	        this.portworxVolume = this.convertValues(source["portworxVolume"], null);
+	        this.scaleIO = this.convertValues(source["scaleIO"], null);
+	        this.local = this.convertValues(source["local"], null);
+	        this.storageos = this.convertValues(source["storageos"], null);
+	        this.csi = this.convertValues(source["csi"], null);
+	        this.accessModes = source["accessModes"];
+	        this.claimRef = this.convertValues(source["claimRef"], ObjectReference);
+	        this.persistentVolumeReclaimPolicy = source["persistentVolumeReclaimPolicy"];
+	        this.storageClassName = source["storageClassName"];
+	        this.mountOptions = source["mountOptions"];
+	        this.volumeMode = source["volumeMode"];
+	        this.nodeAffinity = this.convertValues(source["nodeAffinity"], VolumeNodeAffinity);
+	        this.volumeAttributesClassName = source["volumeAttributesClassName"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolume {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec?: PersistentVolumeSpec;
+	    status?: PersistentVolumeStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolume(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], PersistentVolumeSpec);
+	        this.status = this.convertValues(source["status"], PersistentVolumeStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumeClaimCondition {
+	    type: string;
+	    status: string;
+	    lastProbeTime?: Time;
+	    lastTransitionTime?: Time;
+	    reason?: string;
+	    message?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeClaimCondition(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.status = source["status"];
+	        this.lastProbeTime = this.convertValues(source["lastProbeTime"], Time);
+	        this.lastTransitionTime = this.convertValues(source["lastTransitionTime"], Time);
+	        this.reason = source["reason"];
+	        this.message = source["message"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumeClaimStatus {
+	    phase?: string;
+	    accessModes?: string[];
+	    capacity?: Record<string, resource.Quantity>;
+	    conditions?: PersistentVolumeClaimCondition[];
+	    allocatedResources?: Record<string, resource.Quantity>;
+	    allocatedResourceStatuses?: Record<string, string>;
+	    currentVolumeAttributesClassName?: string;
+	    modifyVolumeStatus?: ModifyVolumeStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeClaimStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.phase = source["phase"];
+	        this.accessModes = source["accessModes"];
+	        this.capacity = this.convertValues(source["capacity"], resource.Quantity, true);
+	        this.conditions = this.convertValues(source["conditions"], PersistentVolumeClaimCondition);
+	        this.allocatedResources = this.convertValues(source["allocatedResources"], resource.Quantity, true);
+	        this.allocatedResourceStatuses = source["allocatedResourceStatuses"];
+	        this.currentVolumeAttributesClassName = source["currentVolumeAttributesClassName"];
+	        this.modifyVolumeStatus = this.convertValues(source["modifyVolumeStatus"], ModifyVolumeStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PersistentVolumeClaim {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec?: PersistentVolumeClaimSpec;
+	    status?: PersistentVolumeClaimStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new PersistentVolumeClaim(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], PersistentVolumeClaimSpec);
+	        this.status = this.convertValues(source["status"], PersistentVolumeClaimStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+	
+	
+	export class PodResourceClaimStatus {
+	    name: string;
+	    resourceClaimName?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodResourceClaimStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.name = source["name"];
+	        this.resourceClaimName = source["resourceClaimName"];
+	    }
+	}
+	export class PodIP {
+	    ip: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodIP(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.ip = source["ip"];
+	    }
+	}
+	export class PodCondition {
+	    type: string;
+	    status: string;
+	    lastProbeTime?: Time;
+	    lastTransitionTime?: Time;
+	    reason?: string;
+	    message?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new PodCondition(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.status = source["status"];
+	        this.lastProbeTime = this.convertValues(source["lastProbeTime"], Time);
+	        this.lastTransitionTime = this.convertValues(source["lastTransitionTime"], Time);
+	        this.reason = source["reason"];
+	        this.message = source["message"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class PodStatus {
+	    phase?: string;
+	    conditions?: PodCondition[];
+	    message?: string;
+	    reason?: string;
+	    nominatedNodeName?: string;
+	    hostIP?: string;
+	    hostIPs?: HostIP[];
+	    podIP?: string;
+	    podIPs?: PodIP[];
+	    startTime?: Time;
+	    initContainerStatuses?: ContainerStatus[];
+	    containerStatuses?: ContainerStatus[];
+	    qosClass?: string;
+	    ephemeralContainerStatuses?: ContainerStatus[];
+	    resize?: string;
+	    resourceClaimStatuses?: PodResourceClaimStatus[];
+	
+	    static createFrom(source: any = {}) {
+	        return new PodStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.phase = source["phase"];
+	        this.conditions = this.convertValues(source["conditions"], PodCondition);
+	        this.message = source["message"];
+	        this.reason = source["reason"];
+	        this.nominatedNodeName = source["nominatedNodeName"];
+	        this.hostIP = source["hostIP"];
+	        this.hostIPs = this.convertValues(source["hostIPs"], HostIP);
+	        this.podIP = source["podIP"];
+	        this.podIPs = this.convertValues(source["podIPs"], PodIP);
+	        this.startTime = this.convertValues(source["startTime"], Time);
+	        this.initContainerStatuses = this.convertValues(source["initContainerStatuses"], ContainerStatus);
+	        this.containerStatuses = this.convertValues(source["containerStatuses"], ContainerStatus);
+	        this.qosClass = source["qosClass"];
+	        this.ephemeralContainerStatuses = this.convertValues(source["ephemeralContainerStatuses"], ContainerStatus);
+	        this.resize = source["resize"];
+	        this.resourceClaimStatuses = this.convertValues(source["resourceClaimStatuses"], PodResourceClaimStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class Pod {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec?: PodSpec;
+	    status?: PodStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new Pod(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], PodSpec);
+	        this.status = this.convertValues(source["status"], PodStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	export class RollingUpdateStatefulSetStrategy {
+	    partition?: number;
+	    maxUnavailable?: intstr.IntOrString;
+	
+	    static createFrom(source: any = {}) {
+	        return new RollingUpdateStatefulSetStrategy(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.partition = source["partition"];
+	        this.maxUnavailable = this.convertValues(source["maxUnavailable"], intstr.IntOrString);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	export class Secret {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    immutable?: boolean;
+	    data?: Record<string, number[]>;
+	    stringData?: Record<string, string>;
+	    type?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new Secret(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.immutable = source["immutable"];
+	        this.data = source["data"];
+	        this.stringData = source["stringData"];
+	        this.type = source["type"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+	
+	
+	export class StatefulSetCondition {
+	    type: string;
+	    status: string;
+	    lastTransitionTime?: Time;
+	    reason?: string;
+	    message?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSetCondition(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.status = source["status"];
+	        this.lastTransitionTime = this.convertValues(source["lastTransitionTime"], Time);
+	        this.reason = source["reason"];
+	        this.message = source["message"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class StatefulSetStatus {
+	    observedGeneration?: number;
+	    replicas: number;
+	    readyReplicas?: number;
+	    currentReplicas?: number;
+	    updatedReplicas?: number;
+	    currentRevision?: string;
+	    updateRevision?: string;
+	    collisionCount?: number;
+	    conditions?: StatefulSetCondition[];
+	    availableReplicas: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSetStatus(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.observedGeneration = source["observedGeneration"];
+	        this.replicas = source["replicas"];
+	        this.readyReplicas = source["readyReplicas"];
+	        this.currentReplicas = source["currentReplicas"];
+	        this.updatedReplicas = source["updatedReplicas"];
+	        this.currentRevision = source["currentRevision"];
+	        this.updateRevision = source["updateRevision"];
+	        this.collisionCount = source["collisionCount"];
+	        this.conditions = this.convertValues(source["conditions"], StatefulSetCondition);
+	        this.availableReplicas = source["availableReplicas"];
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class StatefulSetOrdinals {
+	    start: number;
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSetOrdinals(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.start = source["start"];
+	    }
+	}
+	export class StatefulSetPersistentVolumeClaimRetentionPolicy {
+	    whenDeleted?: string;
+	    whenScaled?: string;
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSetPersistentVolumeClaimRetentionPolicy(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.whenDeleted = source["whenDeleted"];
+	        this.whenScaled = source["whenScaled"];
+	    }
+	}
+	export class StatefulSetUpdateStrategy {
+	    type?: string;
+	    rollingUpdate?: RollingUpdateStatefulSetStrategy;
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSetUpdateStrategy(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.type = source["type"];
+	        this.rollingUpdate = this.convertValues(source["rollingUpdate"], RollingUpdateStatefulSetStrategy);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class StatefulSetSpec {
+	    replicas?: number;
+	    selector?: LabelSelector;
+	    template: PodTemplateSpec;
+	    volumeClaimTemplates?: PersistentVolumeClaim[];
+	    serviceName: string;
+	    podManagementPolicy?: string;
+	    updateStrategy?: StatefulSetUpdateStrategy;
+	    revisionHistoryLimit?: number;
+	    minReadySeconds?: number;
+	    persistentVolumeClaimRetentionPolicy?: StatefulSetPersistentVolumeClaimRetentionPolicy;
+	    ordinals?: StatefulSetOrdinals;
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSetSpec(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.replicas = source["replicas"];
+	        this.selector = this.convertValues(source["selector"], LabelSelector);
+	        this.template = this.convertValues(source["template"], PodTemplateSpec);
+	        this.volumeClaimTemplates = this.convertValues(source["volumeClaimTemplates"], PersistentVolumeClaim);
+	        this.serviceName = source["serviceName"];
+	        this.podManagementPolicy = source["podManagementPolicy"];
+	        this.updateStrategy = this.convertValues(source["updateStrategy"], StatefulSetUpdateStrategy);
+	        this.revisionHistoryLimit = source["revisionHistoryLimit"];
+	        this.minReadySeconds = source["minReadySeconds"];
+	        this.persistentVolumeClaimRetentionPolicy = this.convertValues(source["persistentVolumeClaimRetentionPolicy"], StatefulSetPersistentVolumeClaimRetentionPolicy);
+	        this.ordinals = this.convertValues(source["ordinals"], StatefulSetOrdinals);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class StatefulSet {
+	    kind?: string;
+	    apiVersion?: string;
+	    name?: string;
+	    generateName?: string;
+	    namespace?: string;
+	    selfLink?: string;
+	    uid?: string;
+	    resourceVersion?: string;
+	    generation?: number;
+	    creationTimestamp?: Time;
+	    deletionTimestamp?: Time;
+	    deletionGracePeriodSeconds?: number;
+	    labels?: Record<string, string>;
+	    annotations?: Record<string, string>;
+	    ownerReferences?: OwnerReference[];
+	    finalizers?: string[];
+	    managedFields?: ManagedFieldsEntry[];
+	    spec?: StatefulSetSpec;
+	    status?: StatefulSetStatus;
+	
+	    static createFrom(source: any = {}) {
+	        return new StatefulSet(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.kind = source["kind"];
+	        this.apiVersion = source["apiVersion"];
+	        this.name = source["name"];
+	        this.generateName = source["generateName"];
+	        this.namespace = source["namespace"];
+	        this.selfLink = source["selfLink"];
+	        this.uid = source["uid"];
+	        this.resourceVersion = source["resourceVersion"];
+	        this.generation = source["generation"];
+	        this.creationTimestamp = this.convertValues(source["creationTimestamp"], Time);
+	        this.deletionTimestamp = this.convertValues(source["deletionTimestamp"], Time);
+	        this.deletionGracePeriodSeconds = source["deletionGracePeriodSeconds"];
+	        this.labels = source["labels"];
+	        this.annotations = source["annotations"];
+	        this.ownerReferences = this.convertValues(source["ownerReferences"], OwnerReference);
+	        this.finalizers = source["finalizers"];
+	        this.managedFields = this.convertValues(source["managedFields"], ManagedFieldsEntry);
+	        this.spec = this.convertValues(source["spec"], StatefulSetSpec);
+	        this.status = this.convertValues(source["status"], StatefulSetStatus);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+
+}
+
+export namespace x509 {
+	
+	export class PolicyMapping {
+	    // Go type: OID
+	    IssuerDomainPolicy: any;
+	    // Go type: OID
+	    SubjectDomainPolicy: any;
+	
+	    static createFrom(source: any = {}) {
+	        return new PolicyMapping(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.IssuerDomainPolicy = this.convertValues(source["IssuerDomainPolicy"], null);
+	        this.SubjectDomainPolicy = this.convertValues(source["SubjectDomainPolicy"], null);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+	export class OID {
+	
+	
+	    static createFrom(source: any = {}) {
+	        return new OID(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	
+	    }
+	}
+	export class Certificate {
+	    Raw: number[];
+	    RawTBSCertificate: number[];
+	    RawSubjectPublicKeyInfo: number[];
+	    RawSubject: number[];
+	    RawIssuer: number[];
+	    Signature: number[];
+	    SignatureAlgorithm: number;
+	    PublicKeyAlgorithm: number;
+	    PublicKey: any;
+	    Version: number;
+	    // Go type: big
+	    SerialNumber?: any;
+	    Issuer: pkix.Name;
+	    Subject: pkix.Name;
+	    // Go type: time
+	    NotBefore: any;
+	    // Go type: time
+	    NotAfter: any;
+	    KeyUsage: number;
+	    Extensions: pkix.Extension[];
+	    ExtraExtensions: pkix.Extension[];
+	    UnhandledCriticalExtensions: number[][];
+	    ExtKeyUsage: number[];
+	    UnknownExtKeyUsage: number[][];
+	    BasicConstraintsValid: boolean;
+	    IsCA: boolean;
+	    MaxPathLen: number;
+	    MaxPathLenZero: boolean;
+	    SubjectKeyId: number[];
+	    AuthorityKeyId: number[];
+	    OCSPServer: string[];
+	    IssuingCertificateURL: string[];
+	    DNSNames: string[];
+	    EmailAddresses: string[];
+	    IPAddresses: number[][];
+	    URIs: url.URL[];
+	    PermittedDNSDomainsCritical: boolean;
+	    PermittedDNSDomains: string[];
+	    ExcludedDNSDomains: string[];
+	    PermittedIPRanges: net.IPNet[];
+	    ExcludedIPRanges: net.IPNet[];
+	    PermittedEmailAddresses: string[];
+	    ExcludedEmailAddresses: string[];
+	    PermittedURIDomains: string[];
+	    ExcludedURIDomains: string[];
+	    CRLDistributionPoints: string[];
+	    PolicyIdentifiers: number[][];
+	    Policies: OID[];
+	    InhibitAnyPolicy: number;
+	    InhibitAnyPolicyZero: boolean;
+	    InhibitPolicyMapping: number;
+	    InhibitPolicyMappingZero: boolean;
+	    RequireExplicitPolicy: number;
+	    RequireExplicitPolicyZero: boolean;
+	    PolicyMappings: PolicyMapping[];
+	
+	    static createFrom(source: any = {}) {
+	        return new Certificate(source);
+	    }
+	
+	    constructor(source: any = {}) {
+	        if ('string' === typeof source) source = JSON.parse(source);
+	        this.Raw = source["Raw"];
+	        this.RawTBSCertificate = source["RawTBSCertificate"];
+	        this.RawSubjectPublicKeyInfo = source["RawSubjectPublicKeyInfo"];
+	        this.RawSubject = source["RawSubject"];
+	        this.RawIssuer = source["RawIssuer"];
+	        this.Signature = source["Signature"];
+	        this.SignatureAlgorithm = source["SignatureAlgorithm"];
+	        this.PublicKeyAlgorithm = source["PublicKeyAlgorithm"];
+	        this.PublicKey = source["PublicKey"];
+	        this.Version = source["Version"];
+	        this.SerialNumber = this.convertValues(source["SerialNumber"], null);
+	        this.Issuer = this.convertValues(source["Issuer"], pkix.Name);
+	        this.Subject = this.convertValues(source["Subject"], pkix.Name);
+	        this.NotBefore = this.convertValues(source["NotBefore"], null);
+	        this.NotAfter = this.convertValues(source["NotAfter"], null);
+	        this.KeyUsage = source["KeyUsage"];
+	        this.Extensions = this.convertValues(source["Extensions"], pkix.Extension);
+	        this.ExtraExtensions = this.convertValues(source["ExtraExtensions"], pkix.Extension);
+	        this.UnhandledCriticalExtensions = source["UnhandledCriticalExtensions"];
+	        this.ExtKeyUsage = source["ExtKeyUsage"];
+	        this.UnknownExtKeyUsage = source["UnknownExtKeyUsage"];
+	        this.BasicConstraintsValid = source["BasicConstraintsValid"];
+	        this.IsCA = source["IsCA"];
+	        this.MaxPathLen = source["MaxPathLen"];
+	        this.MaxPathLenZero = source["MaxPathLenZero"];
+	        this.SubjectKeyId = source["SubjectKeyId"];
+	        this.AuthorityKeyId = source["AuthorityKeyId"];
+	        this.OCSPServer = source["OCSPServer"];
+	        this.IssuingCertificateURL = source["IssuingCertificateURL"];
+	        this.DNSNames = source["DNSNames"];
+	        this.EmailAddresses = source["EmailAddresses"];
+	        this.IPAddresses = source["IPAddresses"];
+	        this.URIs = this.convertValues(source["URIs"], url.URL);
+	        this.PermittedDNSDomainsCritical = source["PermittedDNSDomainsCritical"];
+	        this.PermittedDNSDomains = source["PermittedDNSDomains"];
+	        this.ExcludedDNSDomains = source["ExcludedDNSDomains"];
+	        this.PermittedIPRanges = this.convertValues(source["PermittedIPRanges"], net.IPNet);
+	        this.ExcludedIPRanges = this.convertValues(source["ExcludedIPRanges"], net.IPNet);
+	        this.PermittedEmailAddresses = source["PermittedEmailAddresses"];
+	        this.ExcludedEmailAddresses = source["ExcludedEmailAddresses"];
+	        this.PermittedURIDomains = source["PermittedURIDomains"];
+	        this.ExcludedURIDomains = source["ExcludedURIDomains"];
+	        this.CRLDistributionPoints = source["CRLDistributionPoints"];
+	        this.PolicyIdentifiers = source["PolicyIdentifiers"];
+	        this.Policies = this.convertValues(source["Policies"], OID);
+	        this.InhibitAnyPolicy = source["InhibitAnyPolicy"];
+	        this.InhibitAnyPolicyZero = source["InhibitAnyPolicyZero"];
+	        this.InhibitPolicyMapping = source["InhibitPolicyMapping"];
+	        this.InhibitPolicyMappingZero = source["InhibitPolicyMappingZero"];
+	        this.RequireExplicitPolicy = source["RequireExplicitPolicy"];
+	        this.RequireExplicitPolicyZero = source["RequireExplicitPolicyZero"];
+	        this.PolicyMappings = this.convertValues(source["PolicyMappings"], PolicyMapping);
+	    }
+	
+		convertValues(a: any, classs: any, asMap: boolean = false): any {
+		    if (!a) {
+		        return a;
+		    }
+		    if (a.slice && a.map) {
+		        return (a as any[]).map(elem => this.convertValues(elem, classs));
+		    } else if ("object" === typeof a) {
+		        if (asMap) {
+		            for (const key of Object.keys(a)) {
+		                a[key] = new classs(a[key]);
+		            }
+		            return a;
+		        }
+		        return new classs(a);
+		    }
+		    return a;
+		}
+	}
+
+}
+

diff --git a/frontend/wailsjs/go/services/clusterService.d.ts b/frontend/wailsjs/go/services/clusterService.d.ts
line changes: +60/-0
index 0000000..6797c09
--- /dev/null
+++ b/frontend/wailsjs/go/services/clusterService.d.ts
@@ -0,0 +1,60 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+import {types} from '../models';
+import {context} from '../models';
+import {config} from '../models';
+import {logwrap} from '../models';
+
+export function CreateNamespace(arg1:string,arg2:any):Promise<types.Response>;
+
+export function CreateNamespaceYAML(arg1:string,arg2:string):Promise<types.Response>;
+
+export function CreatePod(arg1:string,arg2:any):Promise<types.Response>;
+
+export function CreatePodYAML(arg1:string,arg2:string):Promise<types.Response>;
+
+export function DeleteIngress(arg1:string,arg2:string,arg3:string):Promise<types.Response>;
+
+export function DeleteNamespace(arg1:string,arg2:string):Promise<types.Response>;
+
+export function DeletePod(arg1:string,arg2:string,arg3:string):Promise<types.Response>;
+
+export function DescribeConfigMap(arg1:string,arg2:string,arg3:string):Promise<types.DescribeResponse>;
+
+export function DescribeDeployment(arg1:string,arg2:string,arg3:string):Promise<types.DescribeResponse>;
+
+export function DescribeIngress(arg1:string,arg2:string,arg3:string):Promise<types.DescribeResponse>;
+
+export function DescribeNamespace(arg1:string,arg2:string):Promise<types.DescribeResponse>;
+
+export function DescribePod(arg1:string,arg2:string,arg3:string):Promise<types.DescribeResponse>;
+
+export function DescribeStatefulSet(arg1:string,arg2:string,arg3:string):Promise<types.DescribeResponse>;
+
+export function GetClusters():Promise<types.ClustersResponse>;
+
+export function GetConfigMaps(arg1:string,arg2:string):Promise<types.ConfigMapsResponse>;
+
+export function GetDeployments(arg1:string,arg2:string):Promise<types.DeploymentsResponse>;
+
+export function GetIngresses(arg1:string,arg2:string):Promise<types.IngressesResponse>;
+
+export function GetNamespace(arg1:string,arg2:string):Promise<types.NamespaceResponse>;
+
+export function GetNamespaces(arg1:string):Promise<types.NamespacesResponse>;
+
+export function GetPod(arg1:string,arg2:string,arg3:string):Promise<types.PodResponse>;
+
+export function GetPods(arg1:string,arg2:string):Promise<types.PodsResponse>;
+
+export function GetStatefulSets(arg1:string,arg2:string):Promise<types.StatefulSetsResponse>;
+
+export function RestartDeployment(arg1:string,arg2:string,arg3:string):Promise<types.DeploymentResponse>;
+
+export function RestartStatefulSet(arg1:string,arg2:string,arg3:string):Promise<types.StatefulSetResponse>;
+
+export function Start(arg1:context.Context,arg2:config.Config,arg3:logwrap.LogWrap):Promise<void>;
+
+export function UpdateNamespaceFromYaml(arg1:string,arg2:string,arg3:string):Promise<types.NamespaceResponse>;
+
+export function UpdatePodFromYaml(arg1:string,arg2:string,arg3:string,arg4:string):Promise<types.PodResponse>;

diff --git a/frontend/wailsjs/go/services/clusterService.js b/frontend/wailsjs/go/services/clusterService.js
line changes: +111/-0
index 0000000..57cbfa9
--- /dev/null
+++ b/frontend/wailsjs/go/services/clusterService.js
@@ -0,0 +1,111 @@
+// @ts-check
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export function CreateNamespace(arg1, arg2) {
+  return window['go']['services']['clusterService']['CreateNamespace'](arg1, arg2);
+}
+
+export function CreateNamespaceYAML(arg1, arg2) {
+  return window['go']['services']['clusterService']['CreateNamespaceYAML'](arg1, arg2);
+}
+
+export function CreatePod(arg1, arg2) {
+  return window['go']['services']['clusterService']['CreatePod'](arg1, arg2);
+}
+
+export function CreatePodYAML(arg1, arg2) {
+  return window['go']['services']['clusterService']['CreatePodYAML'](arg1, arg2);
+}
+
+export function DeleteIngress(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['DeleteIngress'](arg1, arg2, arg3);
+}
+
+export function DeleteNamespace(arg1, arg2) {
+  return window['go']['services']['clusterService']['DeleteNamespace'](arg1, arg2);
+}
+
+export function DeletePod(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['DeletePod'](arg1, arg2, arg3);
+}
+
+export function DescribeConfigMap(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['DescribeConfigMap'](arg1, arg2, arg3);
+}
+
+export function DescribeDeployment(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['DescribeDeployment'](arg1, arg2, arg3);
+}
+
+export function DescribeIngress(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['DescribeIngress'](arg1, arg2, arg3);
+}
+
+export function DescribeNamespace(arg1, arg2) {
+  return window['go']['services']['clusterService']['DescribeNamespace'](arg1, arg2);
+}
+
+export function DescribePod(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['DescribePod'](arg1, arg2, arg3);
+}
+
+export function DescribeStatefulSet(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['DescribeStatefulSet'](arg1, arg2, arg3);
+}
+
+export function GetClusters() {
+  return window['go']['services']['clusterService']['GetClusters']();
+}
+
+export function GetConfigMaps(arg1, arg2) {
+  return window['go']['services']['clusterService']['GetConfigMaps'](arg1, arg2);
+}
+
+export function GetDeployments(arg1, arg2) {
+  return window['go']['services']['clusterService']['GetDeployments'](arg1, arg2);
+}
+
+export function GetIngresses(arg1, arg2) {
+  return window['go']['services']['clusterService']['GetIngresses'](arg1, arg2);
+}
+
+export function GetNamespace(arg1, arg2) {
+  return window['go']['services']['clusterService']['GetNamespace'](arg1, arg2);
+}
+
+export function GetNamespaces(arg1) {
+  return window['go']['services']['clusterService']['GetNamespaces'](arg1);
+}
+
+export function GetPod(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['GetPod'](arg1, arg2, arg3);
+}
+
+export function GetPods(arg1, arg2) {
+  return window['go']['services']['clusterService']['GetPods'](arg1, arg2);
+}
+
+export function GetStatefulSets(arg1, arg2) {
+  return window['go']['services']['clusterService']['GetStatefulSets'](arg1, arg2);
+}
+
+export function RestartDeployment(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['RestartDeployment'](arg1, arg2, arg3);
+}
+
+export function RestartStatefulSet(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['RestartStatefulSet'](arg1, arg2, arg3);
+}
+
+export function Start(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['Start'](arg1, arg2, arg3);
+}
+
+export function UpdateNamespaceFromYaml(arg1, arg2, arg3) {
+  return window['go']['services']['clusterService']['UpdateNamespaceFromYaml'](arg1, arg2, arg3);
+}
+
+export function UpdatePodFromYaml(arg1, arg2, arg3, arg4) {
+  return window['go']['services']['clusterService']['UpdatePodFromYaml'](arg1, arg2, arg3, arg4);
+}

diff --git a/frontend/wailsjs/go/services/httpsrvService.d.ts b/frontend/wailsjs/go/services/httpsrvService.d.ts
line changes: +14/-0
index 0000000..0f43988
--- /dev/null
+++ b/frontend/wailsjs/go/services/httpsrvService.d.ts
@@ -0,0 +1,14 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+import {http} from '../models';
+import {context} from '../models';
+import {config} from '../models';
+import {logwrap} from '../models';
+
+export function HandleDeploymentLogs(arg1:http.ResponseWriter,arg2:http.Request):Promise<void>;
+
+export function HandlePodExecWebSocket(arg1:http.ResponseWriter,arg2:http.Request):Promise<void>;
+
+export function HandlePodLogs(arg1:http.ResponseWriter,arg2:http.Request):Promise<void>;
+
+export function Start(arg1:context.Context,arg2:config.Config,arg3:logwrap.LogWrap):Promise<void>;

diff --git a/frontend/wailsjs/go/services/httpsrvService.js b/frontend/wailsjs/go/services/httpsrvService.js
line changes: +19/-0
index 0000000..9f5ca82
--- /dev/null
+++ b/frontend/wailsjs/go/services/httpsrvService.js
@@ -0,0 +1,19 @@
+// @ts-check
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export function HandleDeploymentLogs(arg1, arg2) {
+  return window['go']['services']['httpsrvService']['HandleDeploymentLogs'](arg1, arg2);
+}
+
+export function HandlePodExecWebSocket(arg1, arg2) {
+  return window['go']['services']['httpsrvService']['HandlePodExecWebSocket'](arg1, arg2);
+}
+
+export function HandlePodLogs(arg1, arg2) {
+  return window['go']['services']['httpsrvService']['HandlePodLogs'](arg1, arg2);
+}
+
+export function Start(arg1, arg2, arg3) {
+  return window['go']['services']['httpsrvService']['Start'](arg1, arg2, arg3);
+}

diff --git a/frontend/wailsjs/go/services/persistentVolumeClaimsService.d.ts b/frontend/wailsjs/go/services/persistentVolumeClaimsService.d.ts
line changes: +16/-0
index 0000000..646976c
--- /dev/null
+++ b/frontend/wailsjs/go/services/persistentVolumeClaimsService.d.ts
@@ -0,0 +1,16 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+import {types} from '../models';
+import {context} from '../models';
+import {config} from '../models';
+import {logwrap} from '../models';
+
+export function Create(arg1:string,arg2:any):Promise<types.PersistentVolumeClaimResponse>;
+
+export function Delete(arg1:string,arg2:string,arg3:string):Promise<types.Response>;
+
+export function Describe(arg1:string,arg2:string,arg3:string):Promise<types.DescribeResponse>;
+
+export function List(arg1:string,arg2:string):Promise<types.PersistentVolumeClaimsResponse>;
+
+export function Start(arg1:context.Context,arg2:config.Config,arg3:logwrap.LogWrap):Promise<void>;

diff --git a/frontend/wailsjs/go/services/persistentVolumeClaimsService.js b/frontend/wailsjs/go/services/persistentVolumeClaimsService.js
line changes: +23/-0
index 0000000..0051c80
--- /dev/null
+++ b/frontend/wailsjs/go/services/persistentVolumeClaimsService.js
@@ -0,0 +1,23 @@
+// @ts-check
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export function Create(arg1, arg2) {
+  return window['go']['services']['persistentVolumeClaimsService']['Create'](arg1, arg2);
+}
+
+export function Delete(arg1, arg2, arg3) {
+  return window['go']['services']['persistentVolumeClaimsService']['Delete'](arg1, arg2, arg3);
+}
+
+export function Describe(arg1, arg2, arg3) {
+  return window['go']['services']['persistentVolumeClaimsService']['Describe'](arg1, arg2, arg3);
+}
+
+export function List(arg1, arg2) {
+  return window['go']['services']['persistentVolumeClaimsService']['List'](arg1, arg2);
+}
+
+export function Start(arg1, arg2, arg3) {
+  return window['go']['services']['persistentVolumeClaimsService']['Start'](arg1, arg2, arg3);
+}

diff --git a/frontend/wailsjs/go/services/persistentVolumesService.d.ts b/frontend/wailsjs/go/services/persistentVolumesService.d.ts
line changes: +16/-0
index 0000000..fa4e794
--- /dev/null
+++ b/frontend/wailsjs/go/services/persistentVolumesService.d.ts
@@ -0,0 +1,16 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+import {types} from '../models';
+import {context} from '../models';
+import {config} from '../models';
+import {logwrap} from '../models';
+
+export function Create(arg1:string,arg2:any):Promise<types.Response>;
+
+export function Delete(arg1:string,arg2:string):Promise<types.Response>;
+
+export function Describe(arg1:string,arg2:string,arg3:string):Promise<types.DescribeResponse>;
+
+export function List(arg1:string):Promise<types.PersistentVolumesResponse>;
+
+export function Start(arg1:context.Context,arg2:config.Config,arg3:logwrap.LogWrap):Promise<void>;

diff --git a/frontend/wailsjs/go/services/persistentVolumesService.js b/frontend/wailsjs/go/services/persistentVolumesService.js
line changes: +23/-0
index 0000000..7da10cc
--- /dev/null
+++ b/frontend/wailsjs/go/services/persistentVolumesService.js
@@ -0,0 +1,23 @@
+// @ts-check
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export function Create(arg1, arg2) {
+  return window['go']['services']['persistentVolumesService']['Create'](arg1, arg2);
+}
+
+export function Delete(arg1, arg2) {
+  return window['go']['services']['persistentVolumesService']['Delete'](arg1, arg2);
+}
+
+export function Describe(arg1, arg2, arg3) {
+  return window['go']['services']['persistentVolumesService']['Describe'](arg1, arg2, arg3);
+}
+
+export function List(arg1) {
+  return window['go']['services']['persistentVolumesService']['List'](arg1);
+}
+
+export function Start(arg1, arg2, arg3) {
+  return window['go']['services']['persistentVolumesService']['Start'](arg1, arg2, arg3);
+}

diff --git a/frontend/wailsjs/go/services/secretsService.d.ts b/frontend/wailsjs/go/services/secretsService.d.ts
line changes: +20/-0
index 0000000..a7ecc2f
--- /dev/null
+++ b/frontend/wailsjs/go/services/secretsService.d.ts
@@ -0,0 +1,20 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+import {types} from '../models';
+import {context} from '../models';
+import {config} from '../models';
+import {logwrap} from '../models';
+
+export function Create(arg1:string,arg2:any):Promise<types.Response>;
+
+export function Delete(arg1:string,arg2:string,arg3:string):Promise<types.Response>;
+
+export function Describe(arg1:string,arg2:string,arg3:string):Promise<types.DescribeResponse>;
+
+export function Get(arg1:string,arg2:string,arg3:string):Promise<types.SecretResponse>;
+
+export function List(arg1:string,arg2:string):Promise<types.SecretsResponse>;
+
+export function Start(arg1:context.Context,arg2:config.Config,arg3:logwrap.LogWrap):Promise<void>;
+
+export function Update(arg1:string,arg2:string,arg3:string,arg4:string):Promise<types.SecretResponse>;

diff --git a/frontend/wailsjs/go/services/secretsService.js b/frontend/wailsjs/go/services/secretsService.js
line changes: +31/-0
index 0000000..b0a9a5d
--- /dev/null
+++ b/frontend/wailsjs/go/services/secretsService.js
@@ -0,0 +1,31 @@
+// @ts-check
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export function Create(arg1, arg2) {
+  return window['go']['services']['secretsService']['Create'](arg1, arg2);
+}
+
+export function Delete(arg1, arg2, arg3) {
+  return window['go']['services']['secretsService']['Delete'](arg1, arg2, arg3);
+}
+
+export function Describe(arg1, arg2, arg3) {
+  return window['go']['services']['secretsService']['Describe'](arg1, arg2, arg3);
+}
+
+export function Get(arg1, arg2, arg3) {
+  return window['go']['services']['secretsService']['Get'](arg1, arg2, arg3);
+}
+
+export function List(arg1, arg2) {
+  return window['go']['services']['secretsService']['List'](arg1, arg2);
+}
+
+export function Start(arg1, arg2, arg3) {
+  return window['go']['services']['secretsService']['Start'](arg1, arg2, arg3);
+}
+
+export function Update(arg1, arg2, arg3, arg4) {
+  return window['go']['services']['secretsService']['Update'](arg1, arg2, arg3, arg4);
+}

diff --git a/frontend/wailsjs/runtime/package.json b/frontend/wailsjs/runtime/package.json
line changes: +24/-0
index 0000000..1e7c8a5
--- /dev/null
+++ b/frontend/wailsjs/runtime/package.json
@@ -0,0 +1,24 @@
+{
+  "name": "@wailsapp/runtime",
+  "version": "2.0.0",
+  "description": "Wails Javascript runtime library",
+  "main": "runtime.js",
+  "types": "runtime.d.ts",
+  "scripts": {
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/wailsapp/wails.git"
+  },
+  "keywords": [
+    "Wails",
+    "Javascript",
+    "Go"
+  ],
+  "author": "Lea Anthony <lea.anthony@gmail.com>",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/wailsapp/wails/issues"
+  },
+  "homepage": "https://github.com/wailsapp/wails#readme"
+}

diff --git a/frontend/wailsjs/runtime/runtime.d.ts b/frontend/wailsjs/runtime/runtime.d.ts
line changes: +249/-0
index 0000000..4445dac
--- /dev/null
+++ b/frontend/wailsjs/runtime/runtime.d.ts
@@ -0,0 +1,249 @@
+/*
+ _       __      _ __
+| |     / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__  )
+|__/|__/\__,_/_/_/____/
+The electron alternative for Go
+(c) Lea Anthony 2019-present
+*/
+
+export interface Position {
+    x: number;
+    y: number;
+}
+
+export interface Size {
+    w: number;
+    h: number;
+}
+
+export interface Screen {
+    isCurrent: boolean;
+    isPrimary: boolean;
+    width : number
+    height : number
+}
+
+// Environment information such as platform, buildtype, ...
+export interface EnvironmentInfo {
+    buildType: string;
+    platform: string;
+    arch: string;
+}
+
+// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
+// emits the given event. Optional data may be passed with the event.
+// This will trigger any event listeners.
+export function EventsEmit(eventName: string, ...data: any): void;
+
+// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
+export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
+
+// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
+// sets up a listener for the given event name, but will only trigger a given number times.
+export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
+
+// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
+// sets up a listener for the given event name, but will only trigger once.
+export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
+
+// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
+// unregisters the listener for the given event name.
+export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
+
+// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
+// unregisters all listeners.
+export function EventsOffAll(): void;
+
+// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
+// logs the given message as a raw message
+export function LogPrint(message: string): void;
+
+// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
+// logs the given message at the `trace` log level.
+export function LogTrace(message: string): void;
+
+// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
+// logs the given message at the `debug` log level.
+export function LogDebug(message: string): void;
+
+// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
+// logs the given message at the `error` log level.
+export function LogError(message: string): void;
+
+// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
+// logs the given message at the `fatal` log level.
+// The application will quit after calling this method.
+export function LogFatal(message: string): void;
+
+// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
+// logs the given message at the `info` log level.
+export function LogInfo(message: string): void;
+
+// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
+// logs the given message at the `warning` log level.
+export function LogWarning(message: string): void;
+
+// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
+// Forces a reload by the main application as well as connected browsers.
+export function WindowReload(): void;
+
+// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
+// Reloads the application frontend.
+export function WindowReloadApp(): void;
+
+// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
+// Sets the window AlwaysOnTop or not on top.
+export function WindowSetAlwaysOnTop(b: boolean): void;
+
+// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
+// *Windows only*
+// Sets window theme to system default (dark/light).
+export function WindowSetSystemDefaultTheme(): void;
+
+// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
+// *Windows only*
+// Sets window to light theme.
+export function WindowSetLightTheme(): void;
+
+// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
+// *Windows only*
+// Sets window to dark theme.
+export function WindowSetDarkTheme(): void;
+
+// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
+// Centers the window on the monitor the window is currently on.
+export function WindowCenter(): void;
+
+// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
+// Sets the text in the window title bar.
+export function WindowSetTitle(title: string): void;
+
+// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
+// Makes the window full screen.
+export function WindowFullscreen(): void;
+
+// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
+// Restores the previous window dimensions and position prior to full screen.
+export function WindowUnfullscreen(): void;
+
+// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
+// Returns the state of the window, i.e. whether the window is in full screen mode or not.
+export function WindowIsFullscreen(): Promise<boolean>;
+
+// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
+// Sets the width and height of the window.
+export function WindowSetSize(width: number, height: number): void;
+
+// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
+// Gets the width and height of the window.
+export function WindowGetSize(): Promise<Size>;
+
+// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
+// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
+// Setting a size of 0,0 will disable this constraint.
+export function WindowSetMaxSize(width: number, height: number): void;
+
+// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
+// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
+// Setting a size of 0,0 will disable this constraint.
+export function WindowSetMinSize(width: number, height: number): void;
+
+// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
+// Sets the window position relative to the monitor the window is currently on.
+export function WindowSetPosition(x: number, y: number): void;
+
+// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
+// Gets the window position relative to the monitor the window is currently on.
+export function WindowGetPosition(): Promise<Position>;
+
+// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
+// Hides the window.
+export function WindowHide(): void;
+
+// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
+// Shows the window, if it is currently hidden.
+export function WindowShow(): void;
+
+// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
+// Maximises the window to fill the screen.
+export function WindowMaximise(): void;
+
+// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
+// Toggles between Maximised and UnMaximised.
+export function WindowToggleMaximise(): void;
+
+// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
+// Restores the window to the dimensions and position prior to maximising.
+export function WindowUnmaximise(): void;
+
+// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
+// Returns the state of the window, i.e. whether the window is maximised or not.
+export function WindowIsMaximised(): Promise<boolean>;
+
+// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
+// Minimises the window.
+export function WindowMinimise(): void;
+
+// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
+// Restores the window to the dimensions and position prior to minimising.
+export function WindowUnminimise(): void;
+
+// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
+// Returns the state of the window, i.e. whether the window is minimised or not.
+export function WindowIsMinimised(): Promise<boolean>;
+
+// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
+// Returns the state of the window, i.e. whether the window is normal or not.
+export function WindowIsNormal(): Promise<boolean>;
+
+// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
+// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
+export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
+
+// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
+// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
+export function ScreenGetAll(): Promise<Screen[]>;
+
+// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
+// Opens the given URL in the system browser.
+export function BrowserOpenURL(url: string): void;
+
+// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
+// Returns information about the environment
+export function Environment(): Promise<EnvironmentInfo>;
+
+// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
+// Quits the application.
+export function Quit(): void;
+
+// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
+// Hides the application.
+export function Hide(): void;
+
+// [Show](https://wails.io/docs/reference/runtime/intro#show)
+// Shows the application.
+export function Show(): void;
+
+// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext)
+// Returns the current text stored on clipboard
+export function ClipboardGetText(): Promise<string>;
+
+// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
+// Sets a text on the clipboard
+export function ClipboardSetText(text: string): Promise<boolean>;
+
+// [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop)
+// OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
+export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void
+
+// [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff)
+// OnFileDropOff removes the drag and drop listeners and handlers.
+export function OnFileDropOff() :void
+
+// Check if the file path resolver is available
+export function CanResolveFilePaths(): boolean;
+
+// Resolves file paths for an array of files
+export function ResolveFilePaths(files: File[]): void 
\ No newline at end of file

diff --git a/frontend/wailsjs/runtime/runtime.js b/frontend/wailsjs/runtime/runtime.js
line changes: +238/-0
index 0000000..623397b
--- /dev/null
+++ b/frontend/wailsjs/runtime/runtime.js
@@ -0,0 +1,238 @@
+/*
+ _       __      _ __
+| |     / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__  )
+|__/|__/\__,_/_/_/____/
+The electron alternative for Go
+(c) Lea Anthony 2019-present
+*/
+
+export function LogPrint(message) {
+    window.runtime.LogPrint(message);
+}
+
+export function LogTrace(message) {
+    window.runtime.LogTrace(message);
+}
+
+export function LogDebug(message) {
+    window.runtime.LogDebug(message);
+}
+
+export function LogInfo(message) {
+    window.runtime.LogInfo(message);
+}
+
+export function LogWarning(message) {
+    window.runtime.LogWarning(message);
+}
+
+export function LogError(message) {
+    window.runtime.LogError(message);
+}
+
+export function LogFatal(message) {
+    window.runtime.LogFatal(message);
+}
+
+export function EventsOnMultiple(eventName, callback, maxCallbacks) {
+    return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
+}
+
+export function EventsOn(eventName, callback) {
+    return EventsOnMultiple(eventName, callback, -1);
+}
+
+export function EventsOff(eventName, ...additionalEventNames) {
+    return window.runtime.EventsOff(eventName, ...additionalEventNames);
+}
+
+export function EventsOnce(eventName, callback) {
+    return EventsOnMultiple(eventName, callback, 1);
+}
+
+export function EventsEmit(eventName) {
+    let args = [eventName].slice.call(arguments);
+    return window.runtime.EventsEmit.apply(null, args);
+}
+
+export function WindowReload() {
+    window.runtime.WindowReload();
+}
+
+export function WindowReloadApp() {
+    window.runtime.WindowReloadApp();
+}
+
+export function WindowSetAlwaysOnTop(b) {
+    window.runtime.WindowSetAlwaysOnTop(b);
+}
+
+export function WindowSetSystemDefaultTheme() {
+    window.runtime.WindowSetSystemDefaultTheme();
+}
+
+export function WindowSetLightTheme() {
+    window.runtime.WindowSetLightTheme();
+}
+
+export function WindowSetDarkTheme() {
+    window.runtime.WindowSetDarkTheme();
+}
+
+export function WindowCenter() {
+    window.runtime.WindowCenter();
+}
+
+export function WindowSetTitle(title) {
+    window.runtime.WindowSetTitle(title);
+}
+
+export function WindowFullscreen() {
+    window.runtime.WindowFullscreen();
+}
+
+export function WindowUnfullscreen() {
+    window.runtime.WindowUnfullscreen();
+}
+
+export function WindowIsFullscreen() {
+    return window.runtime.WindowIsFullscreen();
+}
+
+export function WindowGetSize() {
+    return window.runtime.WindowGetSize();
+}
+
+export function WindowSetSize(width, height) {
+    window.runtime.WindowSetSize(width, height);
+}
+
+export function WindowSetMaxSize(width, height) {
+    window.runtime.WindowSetMaxSize(width, height);
+}
+
+export function WindowSetMinSize(width, height) {
+    window.runtime.WindowSetMinSize(width, height);
+}
+
+export function WindowSetPosition(x, y) {
+    window.runtime.WindowSetPosition(x, y);
+}
+
+export function WindowGetPosition() {
+    return window.runtime.WindowGetPosition();
+}
+
+export function WindowHide() {
+    window.runtime.WindowHide();
+}
+
+export function WindowShow() {
+    window.runtime.WindowShow();
+}
+
+export function WindowMaximise() {
+    window.runtime.WindowMaximise();
+}
+
+export function WindowToggleMaximise() {
+    window.runtime.WindowToggleMaximise();
+}
+
+export function WindowUnmaximise() {
+    window.runtime.WindowUnmaximise();
+}
+
+export function WindowIsMaximised() {
+    return window.runtime.WindowIsMaximised();
+}
+
+export function WindowMinimise() {
+    window.runtime.WindowMinimise();
+}
+
+export function WindowUnminimise() {
+    window.runtime.WindowUnminimise();
+}
+
+export function WindowSetBackgroundColour(R, G, B, A) {
+    window.runtime.WindowSetBackgroundColour(R, G, B, A);
+}
+
+export function ScreenGetAll() {
+    return window.runtime.ScreenGetAll();
+}
+
+export function WindowIsMinimised() {
+    return window.runtime.WindowIsMinimised();
+}
+
+export function WindowIsNormal() {
+    return window.runtime.WindowIsNormal();
+}
+
+export function BrowserOpenURL(url) {
+    window.runtime.BrowserOpenURL(url);
+}
+
+export function Environment() {
+    return window.runtime.Environment();
+}
+
+export function Quit() {
+    window.runtime.Quit();
+}
+
+export function Hide() {
+    window.runtime.Hide();
+}
+
+export function Show() {
+    window.runtime.Show();
+}
+
+export function ClipboardGetText() {
+    return window.runtime.ClipboardGetText();
+}
+
+export function ClipboardSetText(text) {
+    return window.runtime.ClipboardSetText(text);
+}
+
+/**
+ * Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
+ *
+ * @export
+ * @callback OnFileDropCallback
+ * @param {number} x - x coordinate of the drop
+ * @param {number} y - y coordinate of the drop
+ * @param {string[]} paths - A list of file paths.
+ */
+
+/**
+ * OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
+ *
+ * @export
+ * @param {OnFileDropCallback} callback - Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
+ * @param {boolean} [useDropTarget=true] - Only call the callback when the drop finished on an element that has the drop target style. (--wails-drop-target)
+ */
+export function OnFileDrop(callback, useDropTarget) {
+    return window.runtime.OnFileDrop(callback, useDropTarget);
+}
+
+/**
+ * OnFileDropOff removes the drag and drop listeners and handlers.
+ */
+export function OnFileDropOff() {
+    return window.runtime.OnFileDropOff();
+}
+
+export function CanResolveFilePaths() {
+    return window.runtime.CanResolveFilePaths();
+}
+
+export function ResolveFilePaths(files) {
+    return window.runtime.ResolveFilePaths(files);
+} 
\ No newline at end of file

diff --git a/go.mod b/go.mod
line changes: +106/-0
index 0000000..46a26fd
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,106 @@
+module kd
+
+go 1.23.0
+
+toolchain go1.23.4
+
+require (
+	github.com/gorilla/websocket v1.5.3
+	github.com/spf13/cobra v1.9.1
+	github.com/spf13/viper v1.20.0
+	github.com/wailsapp/wails/v2 v2.10.1
+	k8s.io/api v0.32.3
+	k8s.io/apimachinery v0.32.3
+	k8s.io/client-go v0.32.3
+	k8s.io/kubectl v0.32.3
+)
+
+require (
+	github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
+	github.com/bep/debounce v1.2.1 // indirect
+	github.com/blang/semver/v4 v4.0.0 // indirect
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
+	github.com/fatih/camelcase v1.0.0 // indirect
+	github.com/fsnotify/fsnotify v1.8.0 // indirect
+	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
+	github.com/go-errors/errors v1.4.2 // indirect
+	github.com/go-logr/logr v1.4.2 // indirect
+	github.com/go-ole/go-ole v1.3.0 // indirect
+	github.com/go-openapi/jsonpointer v0.21.0 // indirect
+	github.com/go-openapi/jsonreference v0.20.2 // indirect
+	github.com/go-openapi/swag v0.23.0 // indirect
+	github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
+	github.com/godbus/dbus/v5 v5.1.0 // indirect
+	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/golang/protobuf v1.5.4 // indirect
+	github.com/google/btree v1.0.1 // indirect
+	github.com/google/gnostic-models v0.6.8 // indirect
+	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/google/gofuzz v1.2.0 // indirect
+	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
+	github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/labstack/echo/v4 v4.13.3 // indirect
+	github.com/labstack/gommon v0.4.2 // indirect
+	github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
+	github.com/leaanthony/gosod v1.0.4 // indirect
+	github.com/leaanthony/slicer v1.6.0 // indirect
+	github.com/leaanthony/u v1.1.1 // indirect
+	github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/moby/spdystream v0.5.0 // indirect
+	github.com/moby/term v0.5.0 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
+	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
+	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
+	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/rivo/uniseg v0.4.7 // indirect
+	github.com/sagikazarmark/locafero v0.7.0 // indirect
+	github.com/samber/lo v1.49.1 // indirect
+	github.com/sourcegraph/conc v0.3.0 // indirect
+	github.com/spf13/afero v1.12.0 // indirect
+	github.com/spf13/cast v1.7.1 // indirect
+	github.com/spf13/pflag v1.0.6 // indirect
+	github.com/subosito/gotenv v1.6.0 // indirect
+	github.com/tkrajina/go-reflector v0.5.8 // indirect
+	github.com/valyala/bytebufferpool v1.0.0 // indirect
+	github.com/valyala/fasttemplate v1.2.2 // indirect
+	github.com/wailsapp/go-webview2 v1.0.19 // indirect
+	github.com/wailsapp/mimetype v1.4.1 // indirect
+	github.com/x448/float16 v0.8.4 // indirect
+	github.com/xlab/treeprint v1.2.0 // indirect
+	go.uber.org/multierr v1.11.0 // indirect
+	golang.org/x/crypto v0.33.0 // indirect
+	golang.org/x/net v0.35.0 // indirect
+	golang.org/x/oauth2 v0.25.0 // indirect
+	golang.org/x/sync v0.11.0 // indirect
+	golang.org/x/sys v0.30.0 // indirect
+	golang.org/x/term v0.29.0 // indirect
+	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/time v0.8.0 // indirect
+	google.golang.org/protobuf v1.36.1 // indirect
+	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+	gopkg.in/inf.v0 v0.9.1 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	k8s.io/cli-runtime v0.32.3 // indirect
+	k8s.io/klog/v2 v2.130.1 // indirect
+	k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
+	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
+	sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
+	sigs.k8s.io/kustomize/api v0.18.0 // indirect
+	sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
+	sigs.k8s.io/yaml v1.4.0 // indirect
+)

diff --git a/go.sum b/go.sum
line changes: +296/-0
index 0000000..72d37e1
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,296 @@
+github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
+github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
+github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
+github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
+github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
+github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
+github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
+github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
+github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
+github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
+github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
+github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
+github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
+github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
+github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
+github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
+github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
+github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
+github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
+github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
+github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
+github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
+github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
+github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
+github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
+github.com/leaanthony/gosod v1.0.4 h1:YLAbVyd591MRffDgxUOU1NwLhT9T1/YiwjKZpkNFeaI=
+github.com/leaanthony/gosod v1.0.4/go.mod h1:GKuIL0zzPj3O1SdWQOdgURSuhkF+Urizzxh26t9f1cw=
+github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
+github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
+github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
+github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
+github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
+github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
+github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
+github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
+github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
+github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
+github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
+github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
+github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
+github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
+github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
+github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
+github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
+github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
+github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
+github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
+github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
+github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
+github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
+github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
+github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
+github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
+github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
+github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
+github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
+github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
+github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
+github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
+github.com/wailsapp/wails/v2 v2.10.1 h1:QWHvWMXII2nI/nXz77gpPG8P3ehl6zKe+u4su5BWIns=
+github.com/wailsapp/wails/v2 v2.10.1/go.mod h1:zrebnFV6MQf9kx8HI4iAv63vsR5v67oS7GTEZ7Pz1TY=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
+github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
+golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
+golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
+golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
+golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
+golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
+google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
+k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
+k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
+k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/cli-runtime v0.32.3 h1:khLF2ivU2T6Q77H97atx3REY9tXiA3OLOjWJxUrdvss=
+k8s.io/cli-runtime v0.32.3/go.mod h1:vZT6dZq7mZAca53rwUfdFSZjdtLyfF61mkf/8q+Xjak=
+k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
+k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
+k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
+k8s.io/kubectl v0.32.3 h1:VMi584rbboso+yjfv0d8uBHwwxbC438LKq+dXd5tOAI=
+k8s.io/kubectl v0.32.3/go.mod h1:6Euv2aso5GKzo/UVMacV6C7miuyevpfI91SvBvV9Zdg=
+k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
+k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
+sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo=
+sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U=
+sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E=
+sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
+sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

diff --git a/main.go b/main.go
line changes: +98/-0
index 0000000..509e676
--- /dev/null
+++ b/main.go
@@ -0,0 +1,98 @@
+package main
+
+import (
+	"context"
+	"embed"
+	"flag"
+	"os"
+	"path"
+
+	"kd/backend/config"
+	"kd/backend/logwrap"
+	"kd/backend/services"
+
+	"github.com/wailsapp/wails/v2"
+	wailslogger "github.com/wailsapp/wails/v2/pkg/logger"
+	"github.com/wailsapp/wails/v2/pkg/options"
+	"github.com/wailsapp/wails/v2/pkg/options/assetserver"
+)
+
+//go:embed all:frontend/dist
+var assets embed.FS
+
+func main() {
+	var err error
+	logger := logwrap.New("logger", os.Stdout, true)
+	logger.SetLevel(logwrap.INFO)
+	wailsLogLevel := wailslogger.INFO
+
+	var (
+		debugFlag = flag.Bool("debug", false, "debug")
+		traceFlag = flag.Bool("trace", false, "trace")
+	)
+	flag.Parse()
+
+	if *debugFlag {
+		logger.SetLevel(logwrap.DEBUG)
+		wailsLogLevel = wailslogger.DEBUG
+	}
+
+	if *traceFlag {
+		wailsLogLevel = wailslogger.TRACE
+	}
+
+	kubeConfigPath, ok := os.LookupEnv("KUBECONFIG")
+	if !ok {
+		homeDir, err := os.UserHomeDir()
+		if err != nil {
+			logger.Error(err.Error())
+			os.Exit(1)
+		}
+		kubeConfigPath = path.Join(homeDir, ".kube", "config.yaml")
+	}
+
+	configOpts := []config.OptFunc{
+		config.WithDebug(*debugFlag),
+	}
+
+	conf, err := config.NewConfig(kubeConfigPath, configOpts...)
+	if err != nil {
+		logger.Error(err.Error())
+		os.Exit(1)
+	}
+
+	clusterSvc := services.Cluster()
+	secretsSvc := services.Secrets()
+	persistentVolumeClaimsSvc := services.PersistentVolumeClaims()
+	persistentVolumesSvc := services.PersistentVolumes()
+	httpsrvSvc := services.Httpsrv()
+
+	err = wails.Run(&options.App{
+		Title:  "kd",
+		Width:  1024,
+		Height: 768,
+		AssetServer: &assetserver.Options{
+			Assets: assets,
+		},
+		BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
+		OnStartup: func(ctx context.Context) {
+			clusterSvc.Start(ctx, conf, logger)
+			secretsSvc.Start(ctx, conf, logger)
+			persistentVolumeClaimsSvc.Start(ctx, conf, logger)
+			persistentVolumesSvc.Start(ctx, conf, logger)
+			httpsrvSvc.Start(ctx, conf, logger)
+		},
+		Bind: []interface{}{
+			clusterSvc,
+			secretsSvc,
+			httpsrvSvc,
+			persistentVolumeClaimsSvc,
+			persistentVolumesSvc,
+		},
+		LogLevel: wailsLogLevel,
+	})
+	if err != nil {
+		logger.Error(err.Error())
+		os.Exit(1)
+	}
+}

diff --git a/wails.json b/wails.json
line changes: +13/-0
index 0000000..7ffd366
--- /dev/null
+++ b/wails.json
@@ -0,0 +1,13 @@
+{
+  "$schema": "https://wails.io/schemas/config.v2.json",
+  "name": "kd",
+  "outputfilename": "kd",
+  "frontend:install": "npm install",
+  "frontend:build": "npm run build",
+  "frontend:dev:watcher": "npm run dev",
+  "frontend:dev:serverUrl": "auto",
+  "author": {
+    "name": "Rene Cunningham",
+    "email": "rene@compounddata.com"
+  }
+}