diff --git a/.github/workflows/deploy-docs-preview.yml b/.github/workflows/deploy-docs-preview.yml index 9029e37dc57..d53f2b149dd 100644 --- a/.github/workflows/deploy-docs-preview.yml +++ b/.github/workflows/deploy-docs-preview.yml @@ -86,8 +86,8 @@ jobs: vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} #Required vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} #Required github-comment: false - # vercel-args: '--local-config vercel.json' # Optional - working-directory: ${{ env.BUILD_PATH }}/build + vercel-args: './build --local-config ./vercel.json' # Optional + working-directory: ${{ env.BUILD_PATH }} docsOutput: needs: [ build ] diff --git a/.github/workflows/deploy-docs-site.yml b/.github/workflows/deploy-docs-site.yml index b7af76412c8..a2d1cdef61c 100644 --- a/.github/workflows/deploy-docs-site.yml +++ b/.github/workflows/deploy-docs-site.yml @@ -69,5 +69,5 @@ jobs: vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} #Required vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} #Required github-comment: false - vercel-args: '--prod' - working-directory: ${{ env.BUILD_PATH }}/build + vercel-args: './build --prod --local-config ./vercel.json' + working-directory: ${{ env.BUILD_PATH }} diff --git a/.github/workflows/services.yml b/.github/workflows/services.yml index 107e49f9266..75b7b2850ba 100644 --- a/.github/workflows/services.yml +++ b/.github/workflows/services.yml @@ -87,7 +87,7 @@ jobs: strategy: matrix: ## TODO: add more modules - module: [database, pay, account, minio, launchpad, exceptionmonitor, aiproxy] + module: [ database, pay, account, minio, launchpad, exceptionmonitor, aiproxy, devbox ] steps: - name: Checkout uses: actions/checkout@v3 @@ -184,7 +184,7 @@ jobs: strategy: matrix: ## TODO: add more modules - module: [database, pay, account, minio, launchpad, exceptionmonitor, aiproxy] + module: [ database, pay, account, minio, launchpad, exceptionmonitor, aiproxy, devbox ] steps: - name: Checkout uses: actions/checkout@v3 diff --git a/README.md b/README.md index 5b35844dfe1..85410d4f902 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Sealos['siːləs] is a cloud operating system distribution based on the Kubernet + 🌐 Visit the [Sealos website](https://sealos.io/) for full documentation and useful links. + 💬 Join our [Discord server](https://discord.gg/qzBmGGZGk7) is to chat with Sealos developers and other Sealos users. This is a good place to learn about Sealos and Kubernetes, ask questions, and share your experiences. -+ 🐦 Tweet at @sealosio on [Twitter](https://twitter.com/sealosio) and follow us. ++ 🐦 Tweet at @Sailos_io on [Twitter](https://twitter.com/Sailos_io) and follow us. + 🐞 Create [GitHub Issues](https://github.com/labring/sealos/issues/new/choose) for bug reports and feature requests. ## 🚧 Roadmap diff --git a/README_zh.md b/README_zh.md index 1336e1cf98b..b10897ce258 100644 --- a/README_zh.md +++ b/README_zh.md @@ -85,7 +85,7 @@ Sealos 是一款以 Kubernetes 为内核的**云操作系统发行版**。它以 - 💬 加入我们的 [Discord服务器](https://discord.gg/qzBmGGZGk7),与 Sealos 开发者和终端用户进行交流。这是了解 Sealos 和 Kubernetes 以及提问和分享经验的理想之地。 -- 🐦 在 [Twitter](https://twitter.com/sealosio) 上关注我们。 +- 🐦 在 [Twitter](https://twitter.com/Sailos_io) 上关注我们。 - 🐞 请将任何 Sealos 的 Bug、问题和需求提交到 [GitHub Issue](https://github.com/labring/sealos/issues/new/choose)。 diff --git a/controllers/account/controllers/payment_controller.go b/controllers/account/controllers/payment_controller.go index 1a016bc44a7..1ae5f1c8dc8 100644 --- a/controllers/account/controllers/payment_controller.go +++ b/controllers/account/controllers/payment_controller.go @@ -23,6 +23,8 @@ import ( "sync" "time" + "github.com/labring/sealos/controllers/pkg/utils/env" + "github.com/google/uuid" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -319,7 +321,7 @@ func (r *PaymentReconciler) reconcileNewPayment(payment *accountv1.Payment) erro if err != nil { return fmt.Errorf("get payment Interface failed: %w", err) } - tradeNO, codeURL, err := payHandler.CreatePayment(payment.Spec.Amount/10000, payment.Spec.UserID, "sealos cloud pay [domain="+r.domain+"]") + tradeNO, codeURL, err := payHandler.CreatePayment(payment.Spec.Amount/10000, payment.Spec.UserID, fmt.Sprintf(env.GetEnvWithDefault("PAY_DESCRIBE_FORMAT", `sealos cloud pay [domain="%s"]`), r.domain)) if err != nil { return fmt.Errorf("get tradeNO and codeURL failed: %w", err) } diff --git a/controllers/devbox/api/v1alpha1/devbox_types.go b/controllers/devbox/api/v1alpha1/devbox_types.go index 5c82533e142..165cbea6660 100644 --- a/controllers/devbox/api/v1alpha1/devbox_types.go +++ b/controllers/devbox/api/v1alpha1/devbox_types.go @@ -18,17 +18,10 @@ package v1alpha1 import ( corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -type ResourceName string - const ( - // ResourceCPU CPU, in cores. (500m = .5 cores) - ResourceCPU ResourceName = "cpu" - // ResourceMemory Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) - ResourceMemory ResourceName = "memory" // FinalizerName is the finalizer for Devbox FinalizerName = "devbox.sealos.io/finalizer" DevBoxPartOf = "devbox" @@ -52,8 +45,6 @@ const ( NetworkTypeTailnet NetworkType = "Tailnet" ) -type ResourceList map[ResourceName]resource.Quantity - type RuntimeRef struct { // +kubebuilder:validation:Required Name string `json:"name"` @@ -69,46 +60,75 @@ type NetworkSpec struct { ExtraPorts []corev1.ContainerPort `json:"extraPorts"` } +type Config struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=devbox + User string `json:"user"` + + // +kubebuilder:validation:Optional + Labels map[string]string `json:"labels,omitempty"` + // +kubebuilder:validation:Optional + Annotations map[string]string `json:"annotations,omitempty"` + + // +kubebuilder:validation:Optional + Command []string `json:"command,omitempty"` + // kubebuilder:validation:Optional + Args []string `json:"args,omitempty"` + // +kubebuilder:validation:Optional + // +kubebuilder:default=/home/devbox/project + WorkingDir string `json:"workingDir,omitempty"` + // +kubebuilder:validation:Optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={/bin/bash,-c} + ReleaseCommand []string `json:"releaseCommand,omitempty"` + // +kubebuilder:validation:Optional + // +kubebuilder:default={/home/devbox/project/entrypoint.sh} + ReleaseArgs []string `json:"releaseArgs,omitempty"` + + // TODO: in v1alpha2 api we need fix the port and app port into one field and create a new type for it. + // +kubebuilder:validation:Optional + // +kubebuilder:default={{name:"devbox-ssh-port",containerPort:22,protocol:TCP}} + Ports []corev1.ContainerPort `json:"ports,omitempty"` + // +kubebuilder:validation:Optional + // +kubebuilder:default={{name:"devbox-app-port",port:8080,protocol:TCP}} + AppPorts []corev1.ServicePort `json:"appPorts,omitempty"` + + // +kubebuilder:validation:Optional + VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` + // +kubebuilder:validation:Optional + Volumes []corev1.Volume `json:"volumes,omitempty"` +} + // DevboxSpec defines the desired state of Devbox type DevboxSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=Running;Stopped State DevboxState `json:"state"` // +kubebuilder:validation:Required - Resource ResourceList `json:"resource"` + Resource corev1.ResourceList `json:"resource"` // +kubebuilder:validation:Optional // +kubebuilder:default=false Squash bool `json:"squash"` // +kubebuilder:validation:Required - RuntimeRef RuntimeRef `json:"runtimeRef"` - - // +kubebuilder:validation:Required - NetworkSpec NetworkSpec `json:"network,omitempty"` + Image string `json:"image"` - // todo add rewrite labels and annotations... - // +kubebuilder:validation:Optional - ExtraLabels map[string]string `json:"extraLabels,omitempty"` // +kubebuilder:validation:Optional - ExtraAnnotations map[string]string `json:"extraAnnotations,omitempty"` + TemplateID string `json:"templateID"` - // +kubebuilder:validation:Optional - Command []string `json:"command,omitempty"` - // +kubebuilder:validation:Optional - Args []string `json:"args,omitempty"` - // +kubebuilder:validation:Optional - WorkingDir string `json:"workingDir,omitempty"` - // todo add rewrite env... - // +kubebuilder:validation:Optional - ExtraEnvs []corev1.EnvVar `json:"extraEnvs"` + // +kubebuilder:validation:Required + Config Config `json:"config"` + + // +kubebuilder:validation:Required + NetworkSpec NetworkSpec `json:"network,omitempty"` - // todo add rewrite volumes and volume mounts.. // +kubebuilder:validation:Optional - ExtraVolumes []corev1.Volume `json:"extraVolumes,omitempty"` + RuntimeClassName string `json:"runtimeClassName,omitempty"` // +kubebuilder:validation:Optional - ExtraVolumeMounts []corev1.VolumeMount `json:"extraVolumeMounts,omitempty"` - + NodeSelector map[string]string `json:"nodeSelector,omitempty"` // +kubebuilder:validation:Optional Tolerations []corev1.Toleration `json:"tolerations,omitempty"` // +kubebuilder:validation:Optional @@ -189,8 +209,6 @@ type DevboxStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="State",type="string",JSONPath=".spec.state" -// +kubebuilder:printcolumn:name="RuntimeRef",type="string",JSONPath=".spec.runtimeRef.name" -// +kubebuilder:printcolumn:name="PodPhase",type="string",JSONPath=".status.podPhase" // +kubebuilder:printcolumn:name="NetworkType",type="string",JSONPath=".status.network.type" // +kubebuilder:printcolumn:name="NodePort",type="integer",JSONPath=".status.network.nodePort" // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" diff --git a/controllers/devbox/api/v1alpha1/runtime_types.go b/controllers/devbox/api/v1alpha1/runtime_types.go deleted file mode 100644 index 52791dbf79b..00000000000 --- a/controllers/devbox/api/v1alpha1/runtime_types.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2024. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type Config struct { - // +kubebuilder:validation:Optional - // +kubebuilder:default=sealos - User string `json:"user"` - - // +kubebuilder:validation:Required - Image string `json:"image"` - - // +kubebuilder:validation:Optional - Labels map[string]string `json:"labels,omitempty"` - // +kubebuilder:validation:Optional - Annotations map[string]string `json:"annotations,omitempty"` - - // +kubebuilder:validation:Optional - Command []string `json:"command,omitempty"` - // kubebuilder:validation:Optional - Args []string `json:"args,omitempty"` - // +kubebuilder:validation:Optional - // +kubebuilder:default=/home/sealos/project - WorkingDir string `json:"workingDir,omitempty"` - // +kubebuilder:validation:Optional - Env []corev1.EnvVar `json:"env,omitempty"` - - // +kubebuilder:validation:Optional - // +kubebuilder:default={/bin/bash,-c} - ReleaseCommand []string `json:"releaseCommand,omitempty"` - // +kubebuilder:validation:Optional - // +kubebuilder:default={/home/sealos/project/entrypoint.sh} - ReleaseArgs []string `json:"releaseArgs,omitempty"` - - // TODO: in v1alpha2 api we need fix the port and app port into one field and create a new type for it. - // +kubebuilder:validation:Optional - // +kubebuilder:default={{name:"devbox-ssh-port",containerPort:22,protocol:TCP}} - Ports []corev1.ContainerPort `json:"ports,omitempty"` - // +kubebuilder:validation:Optional - // +kubebuilder:default={{name:"devbox-app-port",port:8080,protocol:TCP}} - AppPorts []corev1.ServicePort `json:"appPorts,omitempty"` - - // +kubebuilder:validation:Optional - VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` - // +kubebuilder:validation:Optional - Volumes []corev1.Volume `json:"volumes,omitempty"` -} - -type Component struct { - // +kubebuilder:validation:Required - Name string `json:"name"` - // +kubebuilder:validation:Required - Version string `json:"version"` -} - -type RuntimeState string - -const ( - RuntimeStateActive RuntimeState = "active" - RuntimeStateDeprecated RuntimeState = "deprecated" -) - -// RuntimeSpec defines the desired state of Runtime -type RuntimeSpec struct { - // +kubebuilder:validation:Required - ClassRef string `json:"classRef"` - // +kubebuilder:validation:Required - Version string `json:"version"` - - // +kubebuilder:validation:Optional - Components []Component `json:"components,omitempty"` - // +kubebuilder:validation:Optional - Category []string `json:"category,omitempty"` - // +kube:validation:Optional - Description string `json:"description,omitempty"` - - // +kubebuilder:validation:Required - Config Config `json:"config"` - - // +kubebuilder:validation:Optional - RuntimeVersion string `json:"runtimeVersion,omitempty"` - // +kubebuilder:validation:Optional - // +kubebuilder:validation:Enum=active;deprecated - // +kubebuilder:default=active - State RuntimeState `json:"state,omitempty"` -} - -// RuntimeStatus defines the observed state of Runtime -type RuntimeStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="Class",type=string,JSONPath=`.spec.classRef` -// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version` -// +kubebuilder:printcolumn:name="RuntimeVersion",type=string,JSONPath=`.spec.runtimeVersion` -// +kubebuilder:printcolumn:name="State",type=string,JSONPath=`.spec.state` - -// Runtime is the Schema for the runtimes API -type Runtime struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec RuntimeSpec `json:"spec,omitempty"` - Status RuntimeStatus `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// RuntimeList contains a list of Runtime -type RuntimeList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Runtime `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Runtime{}, &RuntimeList{}) -} diff --git a/controllers/devbox/api/v1alpha1/runtimeclass_types.go b/controllers/devbox/api/v1alpha1/runtimeclass_types.go deleted file mode 100644 index 8d8599e5ebd..00000000000 --- a/controllers/devbox/api/v1alpha1/runtimeclass_types.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2024. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type RuntimeClassKind string - -const ( - RuntimeClassOSKind RuntimeClassKind = "OS" - RuntimeClassLanguageKind RuntimeClassKind = "Language" - RuntimeClassFrameworkKind RuntimeClassKind = "Framework" -) - -// RuntimeClassSpec defines the desired state of RuntimeClass -type RuntimeClassSpec struct { - // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum=OS;Language;Framework - Kind RuntimeClassKind `json:"kind"` - // +kubebuilder:validation:Required - Title string `json:"title"` - // +kubebuilder:validation:Optional - Description string `json:"description"` -} - -// RuntimeClassStatus defines the observed state of RuntimeClass -type RuntimeClassStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status - -// RuntimeClass is the Schema for the runtimeclasses API -type RuntimeClass struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec RuntimeClassSpec `json:"spec,omitempty"` - Status RuntimeClassStatus `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// RuntimeClassList contains a list of RuntimeClass -type RuntimeClassList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []RuntimeClass `json:"items"` -} - -func init() { - SchemeBuilder.Register(&RuntimeClass{}, &RuntimeClassList{}) -} diff --git a/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go b/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go index cbf872db25e..d1590df7e11 100644 --- a/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go +++ b/controllers/devbox/api/v1alpha1/zz_generated.deepcopy.go @@ -41,21 +41,6 @@ func (in *CommitHistory) DeepCopy() *CommitHistory { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Component) DeepCopyInto(out *Component) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Component. -func (in *Component) DeepCopy() *Component { - if in == nil { - return nil - } - out := new(Component) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Config) DeepCopyInto(out *Config) { *out = *in @@ -291,58 +276,20 @@ func (in *DevboxSpec) DeepCopyInto(out *DevboxSpec) { *out = *in if in.Resource != nil { in, out := &in.Resource, &out.Resource - *out = make(ResourceList, len(*in)) + *out = make(v1.ResourceList, len(*in)) for key, val := range *in { (*out)[key] = val.DeepCopy() } } - out.RuntimeRef = in.RuntimeRef + in.Config.DeepCopyInto(&out.Config) in.NetworkSpec.DeepCopyInto(&out.NetworkSpec) - if in.ExtraLabels != nil { - in, out := &in.ExtraLabels, &out.ExtraLabels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.ExtraAnnotations != nil { - in, out := &in.ExtraAnnotations, &out.ExtraAnnotations + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } - if in.Command != nil { - in, out := &in.Command, &out.Command - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Args != nil { - in, out := &in.Args, &out.Args - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExtraEnvs != nil { - in, out := &in.ExtraEnvs, &out.ExtraEnvs - *out = make([]v1.EnvVar, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.ExtraVolumes != nil { - in, out := &in.ExtraVolumes, &out.ExtraVolumes - *out = make([]v1.Volume, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.ExtraVolumeMounts != nil { - in, out := &in.ExtraVolumeMounts, &out.ExtraVolumeMounts - *out = make([]v1.VolumeMount, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } if in.Tolerations != nil { in, out := &in.Tolerations, &out.Tolerations *out = make([]v1.Toleration, len(*in)) @@ -520,175 +467,6 @@ func (in *OperationRequestStatus) DeepCopy() *OperationRequestStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in ResourceList) DeepCopyInto(out *ResourceList) { - { - in := &in - *out = make(ResourceList, len(*in)) - for key, val := range *in { - (*out)[key] = val.DeepCopy() - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceList. -func (in ResourceList) DeepCopy() ResourceList { - if in == nil { - return nil - } - out := new(ResourceList) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Runtime) DeepCopyInto(out *Runtime) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Runtime. -func (in *Runtime) DeepCopy() *Runtime { - if in == nil { - return nil - } - out := new(Runtime) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Runtime) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuntimeClass) DeepCopyInto(out *RuntimeClass) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeClass. -func (in *RuntimeClass) DeepCopy() *RuntimeClass { - if in == nil { - return nil - } - out := new(RuntimeClass) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RuntimeClass) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuntimeClassList) DeepCopyInto(out *RuntimeClassList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]RuntimeClass, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeClassList. -func (in *RuntimeClassList) DeepCopy() *RuntimeClassList { - if in == nil { - return nil - } - out := new(RuntimeClassList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RuntimeClassList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuntimeClassSpec) DeepCopyInto(out *RuntimeClassSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeClassSpec. -func (in *RuntimeClassSpec) DeepCopy() *RuntimeClassSpec { - if in == nil { - return nil - } - out := new(RuntimeClassSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuntimeClassStatus) DeepCopyInto(out *RuntimeClassStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeClassStatus. -func (in *RuntimeClassStatus) DeepCopy() *RuntimeClassStatus { - if in == nil { - return nil - } - out := new(RuntimeClassStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuntimeList) DeepCopyInto(out *RuntimeList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Runtime, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeList. -func (in *RuntimeList) DeepCopy() *RuntimeList { - if in == nil { - return nil - } - out := new(RuntimeList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RuntimeList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RuntimeRef) DeepCopyInto(out *RuntimeRef) { *out = *in @@ -703,44 +481,3 @@ func (in *RuntimeRef) DeepCopy() *RuntimeRef { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuntimeSpec) DeepCopyInto(out *RuntimeSpec) { - *out = *in - if in.Components != nil { - in, out := &in.Components, &out.Components - *out = make([]Component, len(*in)) - copy(*out, *in) - } - if in.Category != nil { - in, out := &in.Category, &out.Category - *out = make([]string, len(*in)) - copy(*out, *in) - } - in.Config.DeepCopyInto(&out.Config) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeSpec. -func (in *RuntimeSpec) DeepCopy() *RuntimeSpec { - if in == nil { - return nil - } - out := new(RuntimeSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuntimeStatus) DeepCopyInto(out *RuntimeStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeStatus. -func (in *RuntimeStatus) DeepCopy() *RuntimeStatus { - if in == nil { - return nil - } - out := new(RuntimeStatus) - in.DeepCopyInto(out) - return out -} diff --git a/controllers/devbox/cmd/main.go b/controllers/devbox/cmd/main.go index 62416e9441d..6a9b84efef1 100644 --- a/controllers/devbox/cmd/main.go +++ b/controllers/devbox/cmd/main.go @@ -27,6 +27,7 @@ import ( "k8s.io/client-go/rest" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -42,7 +43,9 @@ import ( devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" "github.com/labring/sealos/controllers/devbox/internal/controller" + "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" "github.com/labring/sealos/controllers/devbox/internal/controller/utils/registry" + utilresource "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" // +kubebuilder:scaffold:imports ) @@ -65,19 +68,24 @@ func main() { var secureMetrics bool var enableHTTP2 bool var tlsOpts []func(*tls.Config) + // debug flag + var debugMode bool + // registry flag var registryAddr string var registryUser string var registryPassword string - var authAddr string + // resource flag var requestCPURate float64 var requestMemoryRate float64 var requestEphemeralStorage string var limitEphemeralStorage string - var debugMode bool - flag.StringVar(®istryAddr, "registry-addr", "sealos.hub:5000", "The address of the registry") - flag.StringVar(®istryUser, "registry-user", "admin", "The user of the registry") - flag.StringVar(®istryPassword, "registry-password", "passw0rd", "The password of the registry") - flag.StringVar(&authAddr, "auth-addr", "sealos.hub:5000", "The address of the auth") + var maximumLimitEphemeralStorage string + // pod matcher flag + var enablePodResourceMatcher bool + var enablePodEnvMatcher bool + var enablePodPortMatcher bool + var enablePodEphemeralStorageMatcher bool + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") @@ -88,11 +96,24 @@ func main() { "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") + // debug flag flag.BoolVar(&debugMode, "debug", false, "If set, debug mode will be enabled") + // registry flag + flag.StringVar(®istryAddr, "registry-addr", "sealos.hub:5000", "The address of the registry") + flag.StringVar(®istryUser, "registry-user", "admin", "The user of the registry") + flag.StringVar(®istryPassword, "registry-password", "passw0rd", "The password of the registry") + // resource flag flag.Float64Var(&requestCPURate, "request-cpu-rate", 10, "The request rate of cpu limit in devbox.") flag.Float64Var(&requestMemoryRate, "request-memory-rate", 10, "The request rate of memory limit in devbox.") - flag.StringVar(&requestEphemeralStorage, "request-ephemeral-storage", "500Mi", "The request value of ephemeral storage in devbox.") - flag.StringVar(&limitEphemeralStorage, "limit-ephemeral-storage", "10Gi", "The limit value of ephemeral storage in devbox.") + flag.StringVar(&requestEphemeralStorage, "request-ephemeral-storage", "500Mi", "The default request value of ephemeral storage in devbox.") + flag.StringVar(&limitEphemeralStorage, "limit-ephemeral-storage", "10Gi", "The default limit value of ephemeral storage in devbox.") + flag.StringVar(&maximumLimitEphemeralStorage, "maximum-limit-ephemeral-storage", "50Gi", "The maximum limit value of ephemeral storage in devbox.") + // pod matcher flag, pod resource matcher, env matcher, port matcher will be enabled by default, ephemeral storage matcher will be disabled by default + flag.BoolVar(&enablePodResourceMatcher, "enable-pod-resource-matcher", true, "If set, pod resource matcher will be enabled") + flag.BoolVar(&enablePodEnvMatcher, "enable-pod-env-matcher", true, "If set, pod env matcher will be enabled") + flag.BoolVar(&enablePodPortMatcher, "enable-pod-port-matcher", true, "If set, pod port matcher will be enabled") + flag.BoolVar(&enablePodEphemeralStorageMatcher, "enable-pod-ephemeral-storage-matcher", false, "If set, pod ephemeral storage matcher will be enabled") + opts := zap.Options{ Development: true, } @@ -182,16 +203,36 @@ func main() { os.Exit(1) } + podMatchers := []matcher.PodMatcher{} + if enablePodResourceMatcher { + podMatchers = append(podMatchers, matcher.ResourceMatcher{}) + } + if enablePodEnvMatcher { + podMatchers = append(podMatchers, matcher.EnvVarMatcher{}) + } + if enablePodPortMatcher { + podMatchers = append(podMatchers, matcher.PortMatcher{}) + } + if enablePodEphemeralStorageMatcher { + podMatchers = append(podMatchers, matcher.EphemeralStorageMatcher{}) + } + if err = (&controller.DevboxReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - CommitImageRegistry: registryAddr, - Recorder: mgr.GetEventRecorderFor("devbox-controller"), - RequestCPURate: requestCPURate, - RequestMemoryRate: requestMemoryRate, - RequestEphemeralStorage: requestEphemeralStorage, - LimitEphemeralStorage: limitEphemeralStorage, - DebugMode: debugMode, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + CommitImageRegistry: registryAddr, + Recorder: mgr.GetEventRecorderFor("devbox-controller"), + RequestRate: utilresource.RequestRate{ + CPU: requestCPURate, + Memory: requestMemoryRate, + }, + EphemeralStorage: utilresource.EphemeralStorage{ + DefaultRequest: resource.MustParse(requestEphemeralStorage), + DefaultLimit: resource.MustParse(limitEphemeralStorage), + MaximumLimit: resource.MustParse(maximumLimitEphemeralStorage), + }, + PodMatchers: podMatchers, + DebugMode: debugMode, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Devbox") os.Exit(1) diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml index eaf11b9e577..0a0d64cb467 100644 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml +++ b/controllers/devbox/config/crd/bases/devbox.sealos.io_devboxes.yaml @@ -32,12 +32,6 @@ spec: - jsonPath: .spec.state name: State type: string - - jsonPath: .spec.runtimeRef.name - name: RuntimeRef - type: string - - jsonPath: .status.podPhase - name: PodPhase - type: string - jsonPath: .status.network.type name: NetworkType type: string @@ -834,1780 +828,1933 @@ spec: type: array type: object type: object - args: - items: - type: string - type: array - command: - items: - type: string - type: array - extraAnnotations: - additionalProperties: - type: string - type: object - extraEnvs: - description: todo add rewrite env... - items: - description: EnvVar represents an environment variable present in - a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: |- - Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in the container and - any service environment variables. If a variable cannot be resolved, - the reference in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether the variable - exists or not. - Defaults to "". + config: + properties: + annotations: + additionalProperties: type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. + type: object + appPorts: + default: + - name: devbox-app-port + port: 8080 + protocol: TCP + items: + description: ServicePort contains information on service's port. properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: + appProtocol: description: |- - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. - properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: + The application protocol for this port. + This is used as a hint for implementations to offer richer behavior for protocols that they understand. + This field follows standard Kubernetes label syntax. + Valid values are either: + + + * Un-prefixed protocol names - reserved for IANA standard service names (as per + RFC-6335 and https://www.iana.org/assignments/service-names). + + + * Kubernetes-defined prefixed names: + * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540 + * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455 + * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455 + + + * Other protocols should use implementation-defined prefixed names such as + mycompany.com/my-custom-protocol. + type: string + name: description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's namespace + The name of this port within the service. This must be a DNS_LABEL. + All ports within a ServiceSpec must have unique names. When considering + the endpoints for a Service, this must match the 'name' field in the + EndpointPort. + Optional if only one ServicePort is defined on this service. + type: string + nodePort: + description: |- + The port on each node on which this service is exposed when type is + NodePort or LoadBalancer. Usually assigned by the system. If a value is + specified, in-range, and not in use it will be used, otherwise the + operation will fail. If not specified, a port will be allocated if this + Service requires one. If this field is specified when creating a + Service which does not need it, creation will fail. This field will be + wiped when updating a Service to no longer need it (e.g. changing type + from NodePort to ClusterIP). + More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: |- + The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". + Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the pods targeted by the service. + Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named port in the + target Pod's container ports. If this is not specified, the value + of the 'port' field is used (an identity map). + This field is ignored for services with clusterIP=None, and should be + omitted or set equal to the 'port' field. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + args: + description: kubebuilder:validation:Optional + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic type: object - x-kubernetes-map-type: atomic + required: + - name type: object - required: - - name - type: object - type: array - extraLabels: - additionalProperties: - type: string - description: todo add rewrite labels and annotations... - type: object - extraVolumeMounts: - items: - description: VolumeMount describes a mounting of a Volume within - a container. - properties: - mountPath: - description: |- - Path within the container at which the volume should be mounted. Must - not contain ':'. - type: string - mountPropagation: - description: |- - mountPropagation determines how mounts are propagated from the host - to container and the other way around. - When not set, MountPropagationNone is used. - This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: |- - Mounted read-only if true, read-write otherwise (false or unspecified). - Defaults to false. - type: boolean - subPath: - description: |- - Path within the volume from which the container's volume should be mounted. - Defaults to "" (volume's root). - type: string - subPathExpr: - description: |- - Expanded path within the volume from which the container's volume should be mounted. - Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. - Defaults to "" (volume's root). - SubPathExpr and SubPath are mutually exclusive. + type: array + labels: + additionalProperties: type: string - required: - - mountPath - - name - type: object - type: array - extraVolumes: - description: todo add rewrite volumes and volume mounts.. - items: - description: Volume represents a named volume in a pod that may - be accessed by any container in the pod. - properties: - awsElasticBlockStore: - description: |- - awsElasticBlockStore represents an AWS Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: object + ports: + default: + - containerPort: 22 + name: devbox-ssh-port + protocol: TCP + description: 'TODO: in v1alpha2 api we need fix the port and app + port into one field and create a new type for it.' + items: + description: ContainerPort represents a network port in a single + container. properties: - fsType: + containerPort: description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from compromising the machine + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. type: string - partition: + hostPort: description: |- - partition is the partition in the volume that you want to mount. - If omitted, the default is to mount by volume name. - Examples: For volume /dev/sda1, you specify the partition as "1". - Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. format: int32 type: integer - readOnly: + name: description: |- - readOnly value true will force the readOnly setting in VolumeMounts. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - type: boolean - volumeID: + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP description: |- - volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". type: string required: - - volumeID + - containerPort type: object - azureDisk: - description: azureDisk represents an Azure Data Disk mount on - the host and bind mount to the pod. + type: array + releaseArgs: + default: + - /home/devbox/project/entrypoint.sh + items: + type: string + type: array + releaseCommand: + default: + - /bin/bash + - -c + items: + type: string + type: array + user: + default: devbox + type: string + volumeMounts: + items: + description: VolumeMount describes a mounting of a Volume within + a container. properties: - cachingMode: - description: 'cachingMode is the Host Caching mode: None, - Read Only, Read Write.' - type: string - diskName: - description: diskName is the Name of the data disk in the - blob storage - type: string - diskURI: - description: diskURI is the URI of data disk in the blob - storage + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. type: string - fsType: + mountPropagation: description: |- - fsType is Filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. type: string - kind: - description: 'kind expected values are Shared: multiple - blob disks per storage account Dedicated: single blob - disk per storage account Managed: azure managed data - disk (only in managed availability set). defaults to shared' + name: + description: This must match the Name of a Volume. type: string readOnly: description: |- - readOnly Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: azureFile represents an Azure File Service mount - on the host and bind mount to the pod. - properties: - readOnly: + subPath: description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretName: - description: secretName is the name of secret that contains - Azure Storage Account Name and Key + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). type: string - shareName: - description: shareName is the azure share Name + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. type: string required: - - secretName - - shareName + - mountPath + - name type: object - cephfs: - description: cephFS represents a Ceph FS mount on the host that - shares a pod's lifetime + type: array + volumes: + items: + description: Volume represents a named volume in a pod that + may be accessed by any container in the pod. properties: - monitors: - description: |- - monitors is Required: Monitors is a collection of Ceph monitors - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - items: - type: string - type: array - path: - description: 'path is Optional: Used as the mounted root, - rather than the full Ceph tree, default is /' - type: string - readOnly: + awsElasticBlockStore: description: |- - readOnly is Optional: Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - type: boolean - secretFile: - description: |- - secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - type: string - secretRef: - description: |- - secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore properties: - name: + fsType: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + format: int32 + type: integer + readOnly: + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: boolean + volumeID: + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore type: string + required: + - volumeID type: object - x-kubernetes-map-type: atomic - user: - description: |- - user is optional: User is the rados user name, default is admin - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - type: string - required: - - monitors - type: object - cinder: - description: |- - cinder represents a cinder volume attached and mounted on kubelets host machine. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: boolean - secretRef: - description: |- - secretRef is optional: points to a secret object containing parameters used to connect - to OpenStack. + azureDisk: + description: azureDisk represents an Azure Data Disk mount + on the host and bind mount to the pod. properties: - name: + cachingMode: + description: 'cachingMode is the Host Caching mode: + None, Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in + the blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the + blob storage + type: string + fsType: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single + blob disk per storage account Managed: azure managed + data disk (only in managed availability set). defaults + to shared' type: string + readOnly: + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI type: object - x-kubernetes-map-type: atomic - volumeID: - description: |- - volumeID used to identify the volume in cinder. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: string - required: - - volumeID - type: object - configMap: - description: configMap represents a configMap that should populate - this volume - properties: - defaultMode: - description: |- - defaultMode is optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - ConfigMap will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional specify whether the ConfigMap or its - keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - description: csi (Container Storage Interface) represents ephemeral - storage that is handled by certain external CSI drivers (Beta - feature). - properties: - driver: - description: |- - driver is the name of the CSI driver that handles this volume. - Consult with your admin for the correct name as registered in the cluster. - type: string - fsType: - description: |- - fsType to mount. Ex. "ext4", "xfs", "ntfs". - If not provided, the empty value is passed to the associated CSI driver - which will determine the default filesystem to apply. - type: string - nodePublishSecretRef: - description: |- - nodePublishSecretRef is a reference to the secret object containing - sensitive information to pass to the CSI driver to complete the CSI - NodePublishVolume and NodeUnpublishVolume calls. - This field is optional, and may be empty if no secret is required. If the - secret object contains more than one secret, all secret references are passed. + azureFile: + description: azureFile represents an Azure File Service + mount on the host and bind mount to the pod. properties: - name: + readOnly: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that + contains Azure Storage Account Name and Key type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName type: object - x-kubernetes-map-type: atomic - readOnly: - description: |- - readOnly specifies a read-only configuration for the volume. - Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: |- - volumeAttributes stores driver-specific properties that are passed to the CSI - driver. Consult your driver's documentation for supported values. + cephfs: + description: cephFS represents a Ceph FS mount on the host + that shares a pod's lifetime + properties: + monitors: + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted + root, rather than the full Ceph tree, default is /' + type: string + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: boolean + secretFile: + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + secretRef: + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + required: + - monitors type: object - required: - - driver - type: object - downwardAPI: - description: downwardAPI represents downward API about the pod - that should populate this volume - properties: - defaultMode: + cinder: description: |- - Optional: mode bits to use on created files by default. Must be a - Optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: Items is a list of downward API volume file - items: - description: DownwardAPIVolumeFile represents information - to create the file containing the pod field - properties: - fieldRef: - description: 'Required: Selects a field of the pod: - only annotations, labels, name and namespace are - supported.' + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: boolean + secretRef: + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should + populate this volume + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a + volume. properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". + key: + description: key is the key to project. type: string - fieldPath: - description: Path of the field to select in the - specified API version. + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - - fieldPath + - key + - path type: object - x-kubernetes-map-type: atomic - mode: - description: |- - Optional: mode bits used to set permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: 'Required: Path is the relative path - name of the file to be created. Must not be absolute - or contain the ''..'' path. Must be utf-8 encoded. - The first item of the relative path must not start - with ''..''' + type: array + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents + ephemeral storage that is handled by certain external + CSI drivers (Beta feature). + properties: + driver: + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: type: string - resourceFieldRef: - description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the + pod that should populate this volume + properties: + defaultMode: + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' + fieldRef: + description: 'Required: Selects a field of the + pod: only annotations, labels, name and namespace + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must not + be absolute or contain the ''..'' path. Must + be utf-8 encoded. The first item of the relative + path must not start with ''..''' type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic required: - - resource + - path type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - type: object - emptyDir: - description: |- - emptyDir represents a temporary directory that shares a pod's lifetime. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - properties: - medium: + type: array + type: object + emptyDir: description: |- - medium represents what type of storage medium should back this directory. - The default is "" which means to use the node's default medium. - Must be an empty string (default) or Memory. + emptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - type: string - sizeLimit: - anyOf: - - type: integer - - type: string + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: description: |- - sizeLimit is the total amount of local storage required for this EmptyDir volume. - The size limit is also applicable for memory medium. - The maximum usage on memory medium EmptyDir would be the minimum value between - the SizeLimit specified here and the sum of memory limits of all containers in a pod. - The default is nil which means that the limit is undefined. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: |- - ephemeral represents a volume that is handled by a cluster storage driver. - The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, - and deleted when the pod is removed. + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. - Use this if: - a) the volume is only needed while the pod runs, - b) features of normal volumes like restoring from snapshot or capacity - tracking are needed, - c) the storage driver is specified through a storage class, and - d) the storage driver supports dynamic volume provisioning through - a PersistentVolumeClaim (see EphemeralVolumeSource for more - information on the connection between this volume type - and PersistentVolumeClaim). + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). - Use PersistentVolumeClaim or one of the vendor-specific - APIs for volumes that persist for longer than the lifecycle - of an individual pod. + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. - Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to - be used that way - see the documentation of the driver for - more information. + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. - A pod can use both types of ephemeral volumes and - persistent volumes at the same time. - properties: - volumeClaimTemplate: - description: |- - Will be used to create a stand-alone PVC to provision the volume. - The pod in which this EphemeralVolumeSource is embedded will be the - owner of the PVC, i.e. the PVC will be deleted together with the - pod. The name of the PVC will be `-` where - `` is the name from the `PodSpec.Volumes` array - entry. Pod validation will reject the pod if the concatenated name - is not valid for a PVC (for example, too long). + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. + properties: + volumeClaimTemplate: + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). - An existing PVC with that name that is not owned by the pod - will *not* be used for the pod to avoid using an unrelated - volume by mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created PVC is - meant to be used by the pod, the PVC has to updated with an - owner reference to the pod once the pod exists. Normally - this should not be necessary, but it may be useful when - manually reconstructing a broken cluster. + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. - This field is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. - Required, must not be nil. - properties: - metadata: - description: |- - May contain labels and annotations that will be copied into the PVC - when creating it. No other fields are allowed and will be rejected during - validation. - type: object - spec: - description: |- - The specification for the PersistentVolumeClaim. The entire content is - copied unchanged into the PVC that gets created from this - template. The same fields as in a PersistentVolumeClaim - are also valid here. + Required, must not be nil. properties: - accessModes: - description: |- - accessModes contains the desired access modes the volume should have. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 - items: - type: string - type: array - dataSource: + metadata: description: |- - dataSource field can be used to specify either: - * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external controller can support the specified data source, - it will create a new volume based on the contents of the specified data source. - When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, - and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. - If the namespace is specified, then dataSourceRef will not be copied to dataSource. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - required: - - kind - - name + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. type: object - x-kubernetes-map-type: atomic - dataSourceRef: + spec: description: |- - dataSourceRef specifies the object from which to populate the volume with data, if a non-empty - volume is desired. This may be any object from a non-empty API group (non - core object) or a PersistentVolumeClaim object. - When this field is specified, volume binding will only succeed if the type of - the specified object matches some installed volume populator or dynamic - provisioner. - This field will replace the functionality of the dataSource field and as such - if both fields are non-empty, they must have the same value. For backwards - compatibility, when namespace isn't specified in dataSourceRef, - both fields (dataSource and dataSourceRef) will be set to the same - value automatically if one of them is empty and the other is non-empty. - When namespace is specified in dataSourceRef, - dataSource isn't set to the same value and must be empty. - There are three important differences between dataSource and dataSourceRef: - * While dataSource only allows two specific types of objects, dataSourceRef - allows any non-core object, as well as PersistentVolumeClaim objects. - * While dataSource ignores disallowed values (dropping them), dataSourceRef - preserves all values, and generates an error if a disallowed value is - specified. - * While dataSource only allows local objects, dataSourceRef allows objects - in any namespaces. - (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. - (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. properties: - apiGroup: + accessModes: description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - namespace: + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 + items: + type: string + type: array + dataSource: description: |- - Namespace is the namespace of resource being referenced - Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: |- - resources represents the minimum resources the volume should have. - If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements - that are lower than previous value but must still be higher than capacity recorded in the - status field of the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources - properties: - claims: + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: description: |- - Claims lists the names of resources, defined in spec.resourceClaims, - that are used by this container. + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource + being referenced + type: string + name: + description: Name is the name of resource + being referenced + type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. - This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. - This field is immutable. It can only be set for containers. - items: - description: ResourceClaim references one - entry in PodSpec.ResourceClaims. - properties: - name: + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references + one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + selector: + description: selector is a label query over + volumes to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: description: |- - Name must match the name of one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes that resource available - inside a container. + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Limits describes the maximum amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + x-kubernetes-map-type: atomic + storageClassName: description: |- - Requests describes the minimum amount of compute resources required. - If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeMode: + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string type: object - selector: - description: selector is a label query over volumes - to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: |- - storageClassName is the name of the StorageClass required by the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 - type: string - volumeMode: + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that + is attached to a kubelet's host machine and then exposed + to the pod. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. + items: + type: string + type: array + type: object + flexVolume: + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use + for this volume. + type: string + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds + extra command options if any.' + type: object + readOnly: + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. + properties: + name: description: |- - volumeMode defines what type of volume is required by the claim. - Value of Filesystem is implied when not included in claim spec. - type: string - volumeName: - description: volumeName is the binding reference - to the PersistentVolume backing this claim. + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string type: object + x-kubernetes-map-type: atomic required: - - spec - type: object - type: object - fc: - description: fc represents a Fibre Channel resource that is - attached to a kubelet's host machine and then exposed to the - pod. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - lun: - description: 'lun is Optional: FC target lun number' - format: int32 - type: integer - readOnly: - description: |- - readOnly is Optional: Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - targetWWNs: - description: 'targetWWNs is Optional: FC target worldwide - names (WWNs)' - items: - type: string - type: array - wwids: - description: |- - wwids Optional: FC volume world wide identifiers (wwids) - Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. - items: - type: string - type: array - type: object - flexVolume: - description: |- - flexVolume represents a generic volume resource that is - provisioned/attached using an exec based plugin. - properties: - driver: - description: driver is the name of the driver to use for - this volume. - type: string - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'options is Optional: this field holds extra - command options if any.' + - driver type: object - readOnly: - description: |- - readOnly is Optional: defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef is Optional: secretRef is reference to the secret object containing - sensitive information to pass to the plugin scripts. This may be - empty if no secret object is specified. If the secret object - contains more than one secret, all secrets are passed to the plugin - scripts. + flocker: + description: flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the Flocker + control service being running properties: - name: + datasetName: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. + This is unique identifier of a Flocker dataset type: string type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - description: flocker represents a Flocker volume attached to - a kubelet's host machine. This depends on the Flocker control - service being running - properties: - datasetName: - description: |- - datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker - should be considered as deprecated - type: string - datasetUUID: - description: datasetUUID is the UUID of the dataset. This - is unique identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: |- - gcePersistentDisk represents a GCE Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - properties: - fsType: - description: |- - fsType is filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - partition: - description: |- - partition is the partition in the volume that you want to mount. - If omitted, the default is to mount by volume name. - Examples: For volume /dev/sda1, you specify the partition as "1". - Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - format: int32 - type: integer - pdName: - description: |- - pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - type: string - readOnly: + gcePersistentDisk: description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - type: boolean - required: - - pdName - type: object - gitRepo: - description: |- - gitRepo represents a git repository at a particular revision. - DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an - EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir - into the Pod's container. - properties: - directory: - description: |- - directory is the target directory name. - Must not contain or start with '..'. If '.' is supplied, the volume directory will be the - git repository. Otherwise, if specified, the volume will contain the git repository in - the subdirectory with the given name. - type: string - repository: - description: repository is the URL - type: string - revision: - description: revision is the commit hash for the specified - revision. - type: string - required: - - repository - type: object - glusterfs: - description: |- - glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. - More info: https://examples.k8s.io/volumes/glusterfs/README.md - properties: - endpoints: - description: |- - endpoints is the endpoint name that details Glusterfs topology. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: string - path: - description: |- - path is the Glusterfs volume path. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: string - readOnly: + properties: + fsType: + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + format: int32 + type: integer + pdName: + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: boolean + required: + - pdName + type: object + gitRepo: description: |- - readOnly here will force the Glusterfs volume to be mounted with read-only permissions. - Defaults to false. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: |- - hostPath represents a pre-existing file or directory on the host - machine that is directly exposed to the container. This is generally - used for system agents or other privileged things that are allowed - to see the host machine. Most containers will NOT need this. - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- - TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not - mount host directories as read/write. - properties: - path: + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. + properties: + directory: + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: description: |- - path of the directory on the host. - If the path is a symlink, it will follow the link to the real path. - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - type: string - type: + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md + properties: + endpoints: + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + path: + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + readOnly: + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: boolean + required: + - endpoints + - path + type: object + hostPath: description: |- - type for HostPath Volume - Defaults to "" + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - type: string - required: - - path - type: object - iscsi: - description: |- - iscsi represents an ISCSI Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://examples.k8s.io/volumes/iscsi/README.md - properties: - chapAuthDiscovery: - description: chapAuthDiscovery defines whether support iSCSI - Discovery CHAP authentication - type: boolean - chapAuthSession: - description: chapAuthSession defines whether support iSCSI - Session CHAP authentication - type: boolean - fsType: - description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - initiatorName: - description: |- - initiatorName is the custom iSCSI Initiator Name. - If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface - : will be created for the connection. - type: string - iqn: - description: iqn is the target iSCSI Qualified Name. - type: string - iscsiInterface: - description: |- - iscsiInterface is the interface Name that uses an iSCSI transport. - Defaults to 'default' (tcp). - type: string - lun: - description: lun represents iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: |- - portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. - type: boolean - secretRef: - description: secretRef is the CHAP Secret for iSCSI target - and initiator authentication + --- + TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not + mount host directories as read/write. properties: - name: + path: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + type: + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath type: string + required: + - path type: object - x-kubernetes-map-type: atomic - targetPortal: + iscsi: description: |- - targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: |- - name of the volume. - Must be a DNS_LABEL and unique within the pod. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - nfs: - description: |- - nfs represents an NFS mount on the host that shares a pod's lifetime - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - properties: - path: + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support + iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support + iSCSI Session CHAP authentication + type: boolean + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + initiatorName: + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI + target and initiator authentication + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: description: |- - path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string - readOnly: + nfs: description: |- - readOnly here will force the NFS export to be mounted with read-only permissions. - Defaults to false. + nfs represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - type: boolean - server: - description: |- - server is the hostname or IP address of the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: |- - persistentVolumeClaimVolumeSource represents a reference to a - PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims - properties: - claimName: + properties: + path: + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + readOnly: + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: boolean + server: + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + required: + - path + - server + type: object + persistentVolumeClaim: description: |- - claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims - type: string - readOnly: - description: |- - readOnly Will force the ReadOnly setting in VolumeMounts. - Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: photonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host machine - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - pdID: - description: pdID is the ID that identifies Photon Controller - persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: portworxVolume represents a portworx volume attached - and mounted on kubelets host machine - properties: - fsType: - description: |- - fSType represents the filesystem type to mount - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - volumeID: - description: volumeID uniquely identifies a Portworx volume - type: string - required: - - volumeID - type: object - projected: - description: projected items for all in one resources secrets, - configmaps, and downward API - properties: - defaultMode: - description: |- - defaultMode are the mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - sources: - description: sources is the list of volume projections - items: - description: Projection that may be projected along with - other supported volume types - properties: - configMap: - description: configMap information about the configMap - data to project - properties: - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - ConfigMap will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional specify whether the ConfigMap - or its keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - downwardAPI: - description: downwardAPI information about the downwardAPI - data to project + properties: + claimName: + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + type: string + readOnly: + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host + machine + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume + attached and mounted on kubelets host machine + properties: + fsType: + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along + with other supported volume types properties: - items: - description: Items is a list of DownwardAPIVolume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects a field - of the pod: only annotations, labels, - name and namespace are supported.' + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: optional specify whether the + ConfigMap or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the + downwardAPI data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, - defaults to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file to + be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 + encoded. The first item of the relative + path must not start with ''..''' type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required + for volumes, optional for env + vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic required: - - fieldPath + - path type: object - x-kubernetes-map-type: atomic - mode: - description: |- - Optional: mode bits used to set permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: 'Required: Path is the relative - path name of the file to be created. Must - not be absolute or contain the ''..'' - path. Must be utf-8 encoded. The first - item of the relative path must not start - with ''..''' - type: string - resourceFieldRef: - description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + type: array + type: object + secret: + description: secret information about the secret + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' + key: + description: key is the key to project. type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults - to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to - select' + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: - - resource + - key + - path type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - type: object - secret: - description: secret information about the secret data - to project - properties: - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - Secret will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the Secret, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional field specify whether the - Secret or its key must be defined - type: boolean + type: array + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: optional field specify whether + the Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information + about the serviceAccountToken data to project + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + required: + - path + type: object type: object - x-kubernetes-map-type: atomic - serviceAccountToken: - description: serviceAccountToken is information about - the serviceAccountToken data to project + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: |- + group to map volume access to + Default is no group + type: string + readOnly: + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes + type: string + tenant: + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin + type: string + user: + description: |- + user to map volume access to + Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + image: + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + keyring: + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + monitors: + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + items: + type: string + type: array + pool: + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: boolean + secretRef: + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool + associated with the protection domain. + type: string + system: + description: system is the name of the storage system + as configured in ScaleIO. + type: string + volumeName: + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a + volume. properties: - audience: - description: |- - audience is the intended audience of the token. A recipient of a token - must identify itself with an identifier specified in the audience of the - token, and otherwise should reject the token. The audience defaults to the - identifier of the apiserver. + key: + description: key is the key to project. type: string - expirationSeconds: + mode: description: |- - expirationSeconds is the requested duration of validity of the service - account token. As the token approaches expiration, the kubelet volume - plugin will proactively rotate the service account token. The kubelet will - start trying to rotate the token if the token is older than 80 percent of - its time to live or if the token is older than 24 hours.Defaults to 1 hour - and must be at least 10 minutes. - format: int64 + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 type: integer path: description: |- - path is the path relative to the mount point of the file to project the - token into. + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. type: string required: + - key - path type: object - type: object - type: array - type: object - quobyte: - description: quobyte represents a Quobyte mount on the host - that shares a pod's lifetime - properties: - group: - description: |- - group to map volume access to - Default is no group - type: string - readOnly: - description: |- - readOnly here will force the Quobyte volume to be mounted with read-only permissions. - Defaults to false. - type: boolean - registry: - description: |- - registry represents a single or multiple Quobyte Registry services - specified as a string as host:port pair (multiple entries are separated with commas) - which acts as the central registry for volumes - type: string - tenant: - description: |- - tenant owning the given Quobyte volume in the Backend - Used with dynamically provisioned Quobyte volumes, value is set by the plugin - type: string - user: - description: |- - user to map volume access to - Defaults to serivceaccount user - type: string - volume: - description: volume is a string that references an already - created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: |- - rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. - More info: https://examples.k8s.io/volumes/rbd/README.md - properties: - fsType: - description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - image: - description: |- - image is the rados image name. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - keyring: - description: |- - keyring is the path to key ring for RBDUser. - Default is /etc/ceph/keyring. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - monitors: - description: |- - monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - items: - type: string - type: array - pool: - description: |- - pool is the rados pool name. - Default is rbd. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - readOnly: - description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: boolean - secretRef: - description: |- - secretRef is name of the authentication secret for RBDUser. If provided - overrides keyring. - Default is nil. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - properties: - name: + type: array + optional: + description: optional field specify whether the Secret + or its keys must be defined + type: boolean + secretName: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret type: string type: object - x-kubernetes-map-type: atomic - user: - description: |- - user is the rados user name. - Default is admin. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - required: - - image - - monitors - type: object - scaleIO: - description: scaleIO represents a ScaleIO persistent volume - attached and mounted on Kubernetes nodes. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". - Default is "xfs". - type: string - gateway: - description: gateway is the host address of the ScaleIO - API Gateway. - type: string - protectionDomain: - description: protectionDomain is the name of the ScaleIO - Protection Domain for the configured storage. - type: string - readOnly: - description: |- - readOnly Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef references to the secret for ScaleIO user and other - sensitive information. If this is not provided, Login operation will fail. + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. properties: - name: + fsType: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. + type: string + volumeNamespace: + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. type: string type: object - x-kubernetes-map-type: atomic - sslEnabled: - description: sslEnabled Flag enable/disable SSL communication - with Gateway, default false - type: boolean - storageMode: - description: |- - storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. - Default is ThinProvisioned. - type: string - storagePool: - description: storagePool is the ScaleIO Storage Pool associated - with the protection domain. - type: string - system: - description: system is the name of the storage system as - configured in ScaleIO. - type: string - volumeName: - description: |- - volumeName is the name of a volume already created in the ScaleIO system - that is associated with this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: |- - secret represents a secret that should populate this volume. - More info: https://kubernetes.io/docs/concepts/storage/volumes#secret - properties: - defaultMode: - description: |- - defaultMode is Optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values - for mode bits. Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: |- - items If unspecified, each key-value pair in the Data field of the referenced - Secret will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the Secret, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: optional field specify whether the Secret or - its keys must be defined - type: boolean - secretName: - description: |- - secretName is the name of the secret in the pod's namespace to use. - More info: https://kubernetes.io/docs/concepts/storage/volumes#secret - type: string - type: object - storageos: - description: storageOS represents a StorageOS volume attached - and mounted on Kubernetes nodes. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef specifies the secret to use for obtaining the StorageOS API - credentials. If not specified, default values will be attempted. + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine properties: - name: + fsType: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy + Based Management (SPBM) profile name. type: string + volumePath: + description: volumePath is the path that identifies + vSphere volume vmdk + type: string + required: + - volumePath type: object - x-kubernetes-map-type: atomic - volumeName: - description: |- - volumeName is the human-readable name of the StorageOS volume. Volume - names are only unique within a namespace. - type: string - volumeNamespace: - description: |- - volumeNamespace specifies the scope of the volume within StorageOS. If no - namespace is specified then the Pod's namespace will be used. This allows the - Kubernetes name scoping to be mirrored within StorageOS for tighter integration. - Set VolumeName to any name to override the default behaviour. - Set to "default" if you are not using namespaces within StorageOS. - Namespaces that do not pre-exist within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: vsphereVolume represents a vSphere volume attached - and mounted on kubelets host machine - properties: - fsType: - description: |- - fsType is filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - storagePolicyID: - description: storagePolicyID is the storage Policy Based - Management (SPBM) profile ID associated with the StoragePolicyName. - type: string - storagePolicyName: - description: storagePolicyName is the storage Policy Based - Management (SPBM) profile name. - type: string - volumePath: - description: volumePath is the path that identifies vSphere - volume vmdk - type: string required: - - volumePath + - name type: object - required: - - name - type: object - type: array + type: array + workingDir: + default: /home/devbox/project + type: string + type: object + image: + type: string network: properties: extraPorts: @@ -2656,6 +2803,10 @@ spec: required: - type type: object + nodeSelector: + additionalProperties: + type: string + type: object resource: additionalProperties: anyOf: @@ -2663,16 +2814,10 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + description: ResourceList is a set of (resource name, quantity) pairs. type: object - runtimeRef: - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object + runtimeClassName: + type: string squash: default: false type: boolean @@ -2681,6 +2826,8 @@ spec: - Running - Stopped type: string + templateID: + type: string tolerations: items: description: |- @@ -2719,11 +2866,10 @@ spec: type: string type: object type: array - workingDir: - type: string required: + - config + - image - resource - - runtimeRef - state type: object status: diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimeclasses.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimeclasses.yaml deleted file mode 100644 index 29513a0b9b8..00000000000 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimeclasses.yaml +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: runtimeclasses.devbox.sealos.io -spec: - group: devbox.sealos.io - names: - kind: RuntimeClass - listKind: RuntimeClassList - plural: runtimeclasses - singular: runtimeclass - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: RuntimeClass is the Schema for the runtimeclasses API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: RuntimeClassSpec defines the desired state of RuntimeClass - properties: - description: - type: string - kind: - enum: - - OS - - Language - - Framework - type: string - title: - type: string - required: - - kind - - title - type: object - status: - description: RuntimeClassStatus defines the observed state of RuntimeClass - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimes.yaml b/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimes.yaml deleted file mode 100644 index 00ed31895c3..00000000000 --- a/controllers/devbox/config/crd/bases/devbox.sealos.io_runtimes.yaml +++ /dev/null @@ -1,2040 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: runtimes.devbox.sealos.io -spec: - group: devbox.sealos.io - names: - kind: Runtime - listKind: RuntimeList - plural: runtimes - singular: runtime - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.classRef - name: Class - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.runtimeVersion - name: RuntimeVersion - type: string - - jsonPath: .spec.state - name: State - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Runtime is the Schema for the runtimes API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: RuntimeSpec defines the desired state of Runtime - properties: - category: - items: - type: string - type: array - classRef: - type: string - components: - items: - properties: - name: - type: string - version: - type: string - required: - - name - - version - type: object - type: array - config: - properties: - annotations: - additionalProperties: - type: string - type: object - appPorts: - default: - - name: devbox-app-port - port: 8080 - protocol: TCP - items: - description: ServicePort contains information on service's port. - properties: - appProtocol: - description: |- - The application protocol for this port. - This is used as a hint for implementations to offer richer behavior for protocols that they understand. - This field follows standard Kubernetes label syntax. - Valid values are either: - - - * Un-prefixed protocol names - reserved for IANA standard service names (as per - RFC-6335 and https://www.iana.org/assignments/service-names). - - - * Kubernetes-defined prefixed names: - * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540 - * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455 - * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455 - - - * Other protocols should use implementation-defined prefixed names such as - mycompany.com/my-custom-protocol. - type: string - name: - description: |- - The name of this port within the service. This must be a DNS_LABEL. - All ports within a ServiceSpec must have unique names. When considering - the endpoints for a Service, this must match the 'name' field in the - EndpointPort. - Optional if only one ServicePort is defined on this service. - type: string - nodePort: - description: |- - The port on each node on which this service is exposed when type is - NodePort or LoadBalancer. Usually assigned by the system. If a value is - specified, in-range, and not in use it will be used, otherwise the - operation will fail. If not specified, a port will be allocated if this - Service requires one. If this field is specified when creating a - Service which does not need it, creation will fail. This field will be - wiped when updating a Service to no longer need it (e.g. changing type - from NodePort to ClusterIP). - More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport - format: int32 - type: integer - port: - description: The port that will be exposed by this service. - format: int32 - type: integer - protocol: - default: TCP - description: |- - The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". - Default is TCP. - type: string - targetPort: - anyOf: - - type: integer - - type: string - description: |- - Number or name of the port to access on the pods targeted by the service. - Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. - If this is a string, it will be looked up as a named port in the - target Pod's container ports. If this is not specified, the value - of the 'port' field is used (an identity map). - This field is ignored for services with clusterIP=None, and should be - omitted or set equal to the 'port' field. - More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - args: - description: kubebuilder:validation:Optional - items: - type: string - type: array - command: - items: - type: string - type: array - env: - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: Name of the environment variable. Must be a - C_IDENTIFIER. - type: string - value: - description: |- - Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in the container and - any service environment variables. If a variable cannot be resolved, - the reference in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether the variable - exists or not. - Defaults to "". - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: |- - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the - specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's - namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - image: - type: string - labels: - additionalProperties: - type: string - type: object - ports: - default: - - containerPort: 22 - name: devbox-ssh-port - protocol: TCP - description: 'TODO: in v1alpha2 api we need fix the port and app - port into one field and create a new type for it.' - items: - description: ContainerPort represents a network port in a single - container. - properties: - containerPort: - description: |- - Number of port to expose on the pod's IP address. - This must be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external port to. - type: string - hostPort: - description: |- - Number of port to expose on the host. - If specified, this must be a valid port number, 0 < x < 65536. - If HostNetwork is specified, this must match ContainerPort. - Most containers do not need this. - format: int32 - type: integer - name: - description: |- - If specified, this must be an IANA_SVC_NAME and unique within the pod. Each - named port in a pod must have a unique name. Name for the port that can be - referred to by services. - type: string - protocol: - default: TCP - description: |- - Protocol for port. Must be UDP, TCP, or SCTP. - Defaults to "TCP". - type: string - required: - - containerPort - type: object - type: array - releaseArgs: - default: - - /home/sealos/project/entrypoint.sh - items: - type: string - type: array - releaseCommand: - default: - - /bin/bash - - -c - items: - type: string - type: array - user: - default: sealos - type: string - volumeMounts: - items: - description: VolumeMount describes a mounting of a Volume within - a container. - properties: - mountPath: - description: |- - Path within the container at which the volume should be mounted. Must - not contain ':'. - type: string - mountPropagation: - description: |- - mountPropagation determines how mounts are propagated from the host - to container and the other way around. - When not set, MountPropagationNone is used. - This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: |- - Mounted read-only if true, read-write otherwise (false or unspecified). - Defaults to false. - type: boolean - subPath: - description: |- - Path within the volume from which the container's volume should be mounted. - Defaults to "" (volume's root). - type: string - subPathExpr: - description: |- - Expanded path within the volume from which the container's volume should be mounted. - Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. - Defaults to "" (volume's root). - SubPathExpr and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - description: Volume represents a named volume in a pod that - may be accessed by any container in the pod. - properties: - awsElasticBlockStore: - description: |- - awsElasticBlockStore represents an AWS Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - properties: - fsType: - description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - partition: - description: |- - partition is the partition in the volume that you want to mount. - If omitted, the default is to mount by volume name. - Examples: For volume /dev/sda1, you specify the partition as "1". - Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). - format: int32 - type: integer - readOnly: - description: |- - readOnly value true will force the readOnly setting in VolumeMounts. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - type: boolean - volumeID: - description: |- - volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - type: string - required: - - volumeID - type: object - azureDisk: - description: azureDisk represents an Azure Data Disk mount - on the host and bind mount to the pod. - properties: - cachingMode: - description: 'cachingMode is the Host Caching mode: - None, Read Only, Read Write.' - type: string - diskName: - description: diskName is the Name of the data disk in - the blob storage - type: string - diskURI: - description: diskURI is the URI of data disk in the - blob storage - type: string - fsType: - description: |- - fsType is Filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - kind: - description: 'kind expected values are Shared: multiple - blob disks per storage account Dedicated: single - blob disk per storage account Managed: azure managed - data disk (only in managed availability set). defaults - to shared' - type: string - readOnly: - description: |- - readOnly Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: azureFile represents an Azure File Service - mount on the host and bind mount to the pod. - properties: - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretName: - description: secretName is the name of secret that - contains Azure Storage Account Name and Key - type: string - shareName: - description: shareName is the azure share Name - type: string - required: - - secretName - - shareName - type: object - cephfs: - description: cephFS represents a Ceph FS mount on the host - that shares a pod's lifetime - properties: - monitors: - description: |- - monitors is Required: Monitors is a collection of Ceph monitors - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - items: - type: string - type: array - path: - description: 'path is Optional: Used as the mounted - root, rather than the full Ceph tree, default is /' - type: string - readOnly: - description: |- - readOnly is Optional: Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - type: boolean - secretFile: - description: |- - secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - type: string - secretRef: - description: |- - secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - user: - description: |- - user is optional: User is the rados user name, default is admin - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - type: string - required: - - monitors - type: object - cinder: - description: |- - cinder represents a cinder volume attached and mounted on kubelets host machine. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: boolean - secretRef: - description: |- - secretRef is optional: points to a secret object containing parameters used to connect - to OpenStack. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - volumeID: - description: |- - volumeID used to identify the volume in cinder. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: string - required: - - volumeID - type: object - configMap: - description: configMap represents a configMap that should - populate this volume - properties: - defaultMode: - description: |- - defaultMode is optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - ConfigMap will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a - volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional specify whether the ConfigMap - or its keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - description: csi (Container Storage Interface) represents - ephemeral storage that is handled by certain external - CSI drivers (Beta feature). - properties: - driver: - description: |- - driver is the name of the CSI driver that handles this volume. - Consult with your admin for the correct name as registered in the cluster. - type: string - fsType: - description: |- - fsType to mount. Ex. "ext4", "xfs", "ntfs". - If not provided, the empty value is passed to the associated CSI driver - which will determine the default filesystem to apply. - type: string - nodePublishSecretRef: - description: |- - nodePublishSecretRef is a reference to the secret object containing - sensitive information to pass to the CSI driver to complete the CSI - NodePublishVolume and NodeUnpublishVolume calls. - This field is optional, and may be empty if no secret is required. If the - secret object contains more than one secret, all secret references are passed. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - readOnly: - description: |- - readOnly specifies a read-only configuration for the volume. - Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: |- - volumeAttributes stores driver-specific properties that are passed to the CSI - driver. Consult your driver's documentation for supported values. - type: object - required: - - driver - type: object - downwardAPI: - description: downwardAPI represents downward API about the - pod that should populate this volume - properties: - defaultMode: - description: |- - Optional: mode bits to use on created files by default. Must be a - Optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: Items is a list of downward API volume - file - items: - description: DownwardAPIVolumeFile represents information - to create the file containing the pod field - properties: - fieldRef: - description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in - the specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - description: |- - Optional: mode bits used to set permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: 'Required: Path is the relative - path name of the file to be created. Must not - be absolute or contain the ''..'' path. Must - be utf-8 encoded. The first item of the relative - path must not start with ''..''' - type: string - resourceFieldRef: - description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. - properties: - containerName: - description: 'Container name: required for - volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of - the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - type: object - emptyDir: - description: |- - emptyDir represents a temporary directory that shares a pod's lifetime. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - properties: - medium: - description: |- - medium represents what type of storage medium should back this directory. - The default is "" which means to use the node's default medium. - Must be an empty string (default) or Memory. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: |- - sizeLimit is the total amount of local storage required for this EmptyDir volume. - The size limit is also applicable for memory medium. - The maximum usage on memory medium EmptyDir would be the minimum value between - the SizeLimit specified here and the sum of memory limits of all containers in a pod. - The default is nil which means that the limit is undefined. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: |- - ephemeral represents a volume that is handled by a cluster storage driver. - The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, - and deleted when the pod is removed. - - - Use this if: - a) the volume is only needed while the pod runs, - b) features of normal volumes like restoring from snapshot or capacity - tracking are needed, - c) the storage driver is specified through a storage class, and - d) the storage driver supports dynamic volume provisioning through - a PersistentVolumeClaim (see EphemeralVolumeSource for more - information on the connection between this volume type - and PersistentVolumeClaim). - - - Use PersistentVolumeClaim or one of the vendor-specific - APIs for volumes that persist for longer than the lifecycle - of an individual pod. - - - Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to - be used that way - see the documentation of the driver for - more information. - - - A pod can use both types of ephemeral volumes and - persistent volumes at the same time. - properties: - volumeClaimTemplate: - description: |- - Will be used to create a stand-alone PVC to provision the volume. - The pod in which this EphemeralVolumeSource is embedded will be the - owner of the PVC, i.e. the PVC will be deleted together with the - pod. The name of the PVC will be `-` where - `` is the name from the `PodSpec.Volumes` array - entry. Pod validation will reject the pod if the concatenated name - is not valid for a PVC (for example, too long). - - - An existing PVC with that name that is not owned by the pod - will *not* be used for the pod to avoid using an unrelated - volume by mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created PVC is - meant to be used by the pod, the PVC has to updated with an - owner reference to the pod once the pod exists. Normally - this should not be necessary, but it may be useful when - manually reconstructing a broken cluster. - - - This field is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. - - - Required, must not be nil. - properties: - metadata: - description: |- - May contain labels and annotations that will be copied into the PVC - when creating it. No other fields are allowed and will be rejected during - validation. - type: object - spec: - description: |- - The specification for the PersistentVolumeClaim. The entire content is - copied unchanged into the PVC that gets created from this - template. The same fields as in a PersistentVolumeClaim - are also valid here. - properties: - accessModes: - description: |- - accessModes contains the desired access modes the volume should have. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 - items: - type: string - type: array - dataSource: - description: |- - dataSource field can be used to specify either: - * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external controller can support the specified data source, - it will create a new volume based on the contents of the specified data source. - When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, - and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. - If the namespace is specified, then dataSourceRef will not be copied to dataSource. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource - being referenced - type: string - name: - description: Name is the name of resource - being referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - description: |- - dataSourceRef specifies the object from which to populate the volume with data, if a non-empty - volume is desired. This may be any object from a non-empty API group (non - core object) or a PersistentVolumeClaim object. - When this field is specified, volume binding will only succeed if the type of - the specified object matches some installed volume populator or dynamic - provisioner. - This field will replace the functionality of the dataSource field and as such - if both fields are non-empty, they must have the same value. For backwards - compatibility, when namespace isn't specified in dataSourceRef, - both fields (dataSource and dataSourceRef) will be set to the same - value automatically if one of them is empty and the other is non-empty. - When namespace is specified in dataSourceRef, - dataSource isn't set to the same value and must be empty. - There are three important differences between dataSource and dataSourceRef: - * While dataSource only allows two specific types of objects, dataSourceRef - allows any non-core object, as well as PersistentVolumeClaim objects. - * While dataSource ignores disallowed values (dropping them), dataSourceRef - preserves all values, and generates an error if a disallowed value is - specified. - * While dataSource only allows local objects, dataSourceRef allows objects - in any namespaces. - (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. - (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource - being referenced - type: string - name: - description: Name is the name of resource - being referenced - type: string - namespace: - description: |- - Namespace is the namespace of resource being referenced - Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: |- - resources represents the minimum resources the volume should have. - If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements - that are lower than previous value but must still be higher than capacity recorded in the - status field of the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources - properties: - claims: - description: |- - Claims lists the names of resources, defined in spec.resourceClaims, - that are used by this container. - - - This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. - - - This field is immutable. It can only be set for containers. - items: - description: ResourceClaim references - one entry in PodSpec.ResourceClaims. - properties: - name: - description: |- - Name must match the name of one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Limits describes the maximum amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Requests describes the minimum amount of compute resources required. - If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - type: object - selector: - description: selector is a label query over - volumes to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: |- - storageClassName is the name of the StorageClass required by the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 - type: string - volumeMode: - description: |- - volumeMode defines what type of volume is required by the claim. - Value of Filesystem is implied when not included in claim spec. - type: string - volumeName: - description: volumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - fc: - description: fc represents a Fibre Channel resource that - is attached to a kubelet's host machine and then exposed - to the pod. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - lun: - description: 'lun is Optional: FC target lun number' - format: int32 - type: integer - readOnly: - description: |- - readOnly is Optional: Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - targetWWNs: - description: 'targetWWNs is Optional: FC target worldwide - names (WWNs)' - items: - type: string - type: array - wwids: - description: |- - wwids Optional: FC volume world wide identifiers (wwids) - Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. - items: - type: string - type: array - type: object - flexVolume: - description: |- - flexVolume represents a generic volume resource that is - provisioned/attached using an exec based plugin. - properties: - driver: - description: driver is the name of the driver to use - for this volume. - type: string - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'options is Optional: this field holds - extra command options if any.' - type: object - readOnly: - description: |- - readOnly is Optional: defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef is Optional: secretRef is reference to the secret object containing - sensitive information to pass to the plugin scripts. This may be - empty if no secret object is specified. If the secret object - contains more than one secret, all secrets are passed to the plugin - scripts. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - description: flocker represents a Flocker volume attached - to a kubelet's host machine. This depends on the Flocker - control service being running - properties: - datasetName: - description: |- - datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker - should be considered as deprecated - type: string - datasetUUID: - description: datasetUUID is the UUID of the dataset. - This is unique identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: |- - gcePersistentDisk represents a GCE Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - properties: - fsType: - description: |- - fsType is filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - partition: - description: |- - partition is the partition in the volume that you want to mount. - If omitted, the default is to mount by volume name. - Examples: For volume /dev/sda1, you specify the partition as "1". - Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - format: int32 - type: integer - pdName: - description: |- - pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - type: string - readOnly: - description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - type: boolean - required: - - pdName - type: object - gitRepo: - description: |- - gitRepo represents a git repository at a particular revision. - DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an - EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir - into the Pod's container. - properties: - directory: - description: |- - directory is the target directory name. - Must not contain or start with '..'. If '.' is supplied, the volume directory will be the - git repository. Otherwise, if specified, the volume will contain the git repository in - the subdirectory with the given name. - type: string - repository: - description: repository is the URL - type: string - revision: - description: revision is the commit hash for the specified - revision. - type: string - required: - - repository - type: object - glusterfs: - description: |- - glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. - More info: https://examples.k8s.io/volumes/glusterfs/README.md - properties: - endpoints: - description: |- - endpoints is the endpoint name that details Glusterfs topology. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: string - path: - description: |- - path is the Glusterfs volume path. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: string - readOnly: - description: |- - readOnly here will force the Glusterfs volume to be mounted with read-only permissions. - Defaults to false. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: |- - hostPath represents a pre-existing file or directory on the host - machine that is directly exposed to the container. This is generally - used for system agents or other privileged things that are allowed - to see the host machine. Most containers will NOT need this. - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- - TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not - mount host directories as read/write. - properties: - path: - description: |- - path of the directory on the host. - If the path is a symlink, it will follow the link to the real path. - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - type: string - type: - description: |- - type for HostPath Volume - Defaults to "" - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - type: string - required: - - path - type: object - iscsi: - description: |- - iscsi represents an ISCSI Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://examples.k8s.io/volumes/iscsi/README.md - properties: - chapAuthDiscovery: - description: chapAuthDiscovery defines whether support - iSCSI Discovery CHAP authentication - type: boolean - chapAuthSession: - description: chapAuthSession defines whether support - iSCSI Session CHAP authentication - type: boolean - fsType: - description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - initiatorName: - description: |- - initiatorName is the custom iSCSI Initiator Name. - If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface - : will be created for the connection. - type: string - iqn: - description: iqn is the target iSCSI Qualified Name. - type: string - iscsiInterface: - description: |- - iscsiInterface is the interface Name that uses an iSCSI transport. - Defaults to 'default' (tcp). - type: string - lun: - description: lun represents iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: |- - portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. - type: boolean - secretRef: - description: secretRef is the CHAP Secret for iSCSI - target and initiator authentication - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - targetPortal: - description: |- - targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: |- - name of the volume. - Must be a DNS_LABEL and unique within the pod. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - nfs: - description: |- - nfs represents an NFS mount on the host that shares a pod's lifetime - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - properties: - path: - description: |- - path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - type: string - readOnly: - description: |- - readOnly here will force the NFS export to be mounted with read-only permissions. - Defaults to false. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - type: boolean - server: - description: |- - server is the hostname or IP address of the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: |- - persistentVolumeClaimVolumeSource represents a reference to a - PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims - properties: - claimName: - description: |- - claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims - type: string - readOnly: - description: |- - readOnly Will force the ReadOnly setting in VolumeMounts. - Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: photonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host - machine - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - pdID: - description: pdID is the ID that identifies Photon Controller - persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: portworxVolume represents a portworx volume - attached and mounted on kubelets host machine - properties: - fsType: - description: |- - fSType represents the filesystem type to mount - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - volumeID: - description: volumeID uniquely identifies a Portworx - volume - type: string - required: - - volumeID - type: object - projected: - description: projected items for all in one resources secrets, - configmaps, and downward API - properties: - defaultMode: - description: |- - defaultMode are the mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - sources: - description: sources is the list of volume projections - items: - description: Projection that may be projected along - with other supported volume types - properties: - configMap: - description: configMap information about the configMap - data to project - properties: - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - ConfigMap will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path - within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional specify whether the - ConfigMap or its keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - downwardAPI: - description: downwardAPI information about the - downwardAPI data to project - properties: - items: - description: Items is a list of DownwardAPIVolume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects a field - of the pod: only annotations, labels, - name and namespace are supported.' - properties: - apiVersion: - description: Version of the schema - the FieldPath is written in terms - of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to - select in the specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - description: |- - Optional: mode bits used to set permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: 'Required: Path is the - relative path name of the file to - be created. Must not be absolute or - contain the ''..'' path. Must be utf-8 - encoded. The first item of the relative - path must not start with ''..''' - type: string - resourceFieldRef: - description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. - properties: - containerName: - description: 'Container name: required - for volumes, optional for env - vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output - format of the exposed resources, - defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - type: object - secret: - description: secret information about the secret - data to project - properties: - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - Secret will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the Secret, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path - within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional field specify whether - the Secret or its key must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - serviceAccountToken: - description: serviceAccountToken is information - about the serviceAccountToken data to project - properties: - audience: - description: |- - audience is the intended audience of the token. A recipient of a token - must identify itself with an identifier specified in the audience of the - token, and otherwise should reject the token. The audience defaults to the - identifier of the apiserver. - type: string - expirationSeconds: - description: |- - expirationSeconds is the requested duration of validity of the service - account token. As the token approaches expiration, the kubelet volume - plugin will proactively rotate the service account token. The kubelet will - start trying to rotate the token if the token is older than 80 percent of - its time to live or if the token is older than 24 hours.Defaults to 1 hour - and must be at least 10 minutes. - format: int64 - type: integer - path: - description: |- - path is the path relative to the mount point of the file to project the - token into. - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - description: quobyte represents a Quobyte mount on the host - that shares a pod's lifetime - properties: - group: - description: |- - group to map volume access to - Default is no group - type: string - readOnly: - description: |- - readOnly here will force the Quobyte volume to be mounted with read-only permissions. - Defaults to false. - type: boolean - registry: - description: |- - registry represents a single or multiple Quobyte Registry services - specified as a string as host:port pair (multiple entries are separated with commas) - which acts as the central registry for volumes - type: string - tenant: - description: |- - tenant owning the given Quobyte volume in the Backend - Used with dynamically provisioned Quobyte volumes, value is set by the plugin - type: string - user: - description: |- - user to map volume access to - Defaults to serivceaccount user - type: string - volume: - description: volume is a string that references an already - created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: |- - rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. - More info: https://examples.k8s.io/volumes/rbd/README.md - properties: - fsType: - description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - image: - description: |- - image is the rados image name. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - keyring: - description: |- - keyring is the path to key ring for RBDUser. - Default is /etc/ceph/keyring. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - monitors: - description: |- - monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - items: - type: string - type: array - pool: - description: |- - pool is the rados pool name. - Default is rbd. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - readOnly: - description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: boolean - secretRef: - description: |- - secretRef is name of the authentication secret for RBDUser. If provided - overrides keyring. - Default is nil. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - user: - description: |- - user is the rados user name. - Default is admin. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - required: - - image - - monitors - type: object - scaleIO: - description: scaleIO represents a ScaleIO persistent volume - attached and mounted on Kubernetes nodes. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". - Default is "xfs". - type: string - gateway: - description: gateway is the host address of the ScaleIO - API Gateway. - type: string - protectionDomain: - description: protectionDomain is the name of the ScaleIO - Protection Domain for the configured storage. - type: string - readOnly: - description: |- - readOnly Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef references to the secret for ScaleIO user and other - sensitive information. If this is not provided, Login operation will fail. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - sslEnabled: - description: sslEnabled Flag enable/disable SSL communication - with Gateway, default false - type: boolean - storageMode: - description: |- - storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. - Default is ThinProvisioned. - type: string - storagePool: - description: storagePool is the ScaleIO Storage Pool - associated with the protection domain. - type: string - system: - description: system is the name of the storage system - as configured in ScaleIO. - type: string - volumeName: - description: |- - volumeName is the name of a volume already created in the ScaleIO system - that is associated with this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: |- - secret represents a secret that should populate this volume. - More info: https://kubernetes.io/docs/concepts/storage/volumes#secret - properties: - defaultMode: - description: |- - defaultMode is Optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values - for mode bits. Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: |- - items If unspecified, each key-value pair in the Data field of the referenced - Secret will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the Secret, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a - volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: optional field specify whether the Secret - or its keys must be defined - type: boolean - secretName: - description: |- - secretName is the name of the secret in the pod's namespace to use. - More info: https://kubernetes.io/docs/concepts/storage/volumes#secret - type: string - type: object - storageos: - description: storageOS represents a StorageOS volume attached - and mounted on Kubernetes nodes. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef specifies the secret to use for obtaining the StorageOS API - credentials. If not specified, default values will be attempted. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - volumeName: - description: |- - volumeName is the human-readable name of the StorageOS volume. Volume - names are only unique within a namespace. - type: string - volumeNamespace: - description: |- - volumeNamespace specifies the scope of the volume within StorageOS. If no - namespace is specified then the Pod's namespace will be used. This allows the - Kubernetes name scoping to be mirrored within StorageOS for tighter integration. - Set VolumeName to any name to override the default behaviour. - Set to "default" if you are not using namespaces within StorageOS. - Namespaces that do not pre-exist within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: vsphereVolume represents a vSphere volume attached - and mounted on kubelets host machine - properties: - fsType: - description: |- - fsType is filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - storagePolicyID: - description: storagePolicyID is the storage Policy Based - Management (SPBM) profile ID associated with the StoragePolicyName. - type: string - storagePolicyName: - description: storagePolicyName is the storage Policy - Based Management (SPBM) profile name. - type: string - volumePath: - description: volumePath is the path that identifies - vSphere volume vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - workingDir: - default: /home/sealos/project - type: string - required: - - image - type: object - description: - type: string - runtimeVersion: - type: string - state: - default: active - enum: - - active - - deprecated - type: string - version: - type: string - required: - - classRef - - config - - version - type: object - status: - description: RuntimeStatus defines the observed state of Runtime - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/controllers/devbox/config/crd/kustomization.yaml b/controllers/devbox/config/crd/kustomization.yaml index 455200e6d9e..37bb4dbbdd9 100644 --- a/controllers/devbox/config/crd/kustomization.yaml +++ b/controllers/devbox/config/crd/kustomization.yaml @@ -17,8 +17,6 @@ # It should be run by config/default resources: - bases/devbox.sealos.io_devboxes.yaml -- bases/devbox.sealos.io_runtimes.yaml -- bases/devbox.sealos.io_runtimeclasses.yaml - bases/devbox.sealos.io_devboxreleases.yaml - bases/devbox.sealos.io_operationrequests.yaml # +kubebuilder:scaffold:crdkustomizeresource @@ -31,8 +29,6 @@ patches: # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- path: patches/cainjection_in_devboxes.yaml -#- path: patches/cainjection_in_runtimes.yaml -#- path: patches/cainjection_in_runtimeclasses.yaml #- path: patches/cainjection_in_devboxreleases.yaml #- path: patches/cainjection_in_operationrequests.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch diff --git a/controllers/devbox/config/manager/manager.yaml b/controllers/devbox/config/manager/manager.yaml index d8098831880..a571e871f39 100644 --- a/controllers/devbox/config/manager/manager.yaml +++ b/controllers/devbox/config/manager/manager.yaml @@ -80,7 +80,6 @@ spec: - --registry-addr={{ .registryAddr }} - --registry-user={{ .registryUser }} - --registry-password={{ .registryPassword }} - - --auth-addr={{ .authAddr }} image: controller:latest name: manager securityContext: diff --git a/controllers/devbox/config/rbac/kustomization.yaml b/controllers/devbox/config/rbac/kustomization.yaml index f7feaa591dc..1a77a2ef46e 100644 --- a/controllers/devbox/config/rbac/kustomization.yaml +++ b/controllers/devbox/config/rbac/kustomization.yaml @@ -40,10 +40,6 @@ resources: - operationrequest_viewer_role.yaml - devboxrelease_editor_role.yaml - devboxrelease_viewer_role.yaml -- runtimeclass_editor_role.yaml -- runtimeclass_viewer_role.yaml -- runtime_editor_role.yaml -- runtime_viewer_role.yaml - devbox_editor_role.yaml - devbox_viewer_role.yaml diff --git a/controllers/devbox/config/rbac/runtime_editor_role.yaml b/controllers/devbox/config/rbac/runtime_editor_role.yaml deleted file mode 100644 index e3645875342..00000000000 --- a/controllers/devbox/config/rbac/runtime_editor_role.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# permissions for end users to edit runtimes. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: runtime-editor-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - runtimes - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimes/status - verbs: - - get diff --git a/controllers/devbox/config/rbac/runtime_viewer_role.yaml b/controllers/devbox/config/rbac/runtime_viewer_role.yaml deleted file mode 100644 index 464ff30775f..00000000000 --- a/controllers/devbox/config/rbac/runtime_viewer_role.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# permissions for end users to view runtimes. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: runtime-viewer-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - runtimes - verbs: - - get - - list - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimes/status - verbs: - - get diff --git a/controllers/devbox/config/rbac/runtimeclass_editor_role.yaml b/controllers/devbox/config/rbac/runtimeclass_editor_role.yaml deleted file mode 100644 index 9483f0bd01a..00000000000 --- a/controllers/devbox/config/rbac/runtimeclass_editor_role.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# permissions for end users to edit runtimeclasses. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: runtimeclass-editor-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses/status - verbs: - - get diff --git a/controllers/devbox/config/rbac/runtimeclass_viewer_role.yaml b/controllers/devbox/config/rbac/runtimeclass_viewer_role.yaml deleted file mode 100644 index 97ba3a15d8f..00000000000 --- a/controllers/devbox/config/rbac/runtimeclass_viewer_role.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# permissions for end users to view runtimeclasses. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: devbox - app.kubernetes.io/managed-by: kustomize - name: runtimeclass-viewer-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses - verbs: - - get - - list - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses/status - verbs: - - get diff --git a/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml b/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml index b0b194ef79c..71e5bf070b0 100644 --- a/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml +++ b/controllers/devbox/config/samples/devbox_v1alpha1_devbox.yaml @@ -18,19 +18,23 @@ metadata: labels: app.kubernetes.io/name: devbox app.kubernetes.io/managed-by: kustomize - name: devbox-sample + name: devbox-gpu-sample spec: state: Running + runtimeClassName: nvidia resource: cpu: 2 memory: 4000Mi + nvidia.com/gpu: 1 runtimeRef: - name: go-1-22-5 + name: go-1-22-5-2024-11-12-0651 namespace: devbox-system + nodeSelector: + nvidia.com/gpu.product: Tesla-P40 network: type: NodePort extraPorts: - containerPort: 443 name: 'https' - containerPort: 80 - name: 'http' \ No newline at end of file + name: 'http' diff --git a/controllers/devbox/config/samples/devbox_v1alpha1_runtime.yaml b/controllers/devbox/config/samples/devbox_v1alpha1_runtime.yaml deleted file mode 100644 index 774a7a5947e..00000000000 --- a/controllers/devbox/config/samples/devbox_v1alpha1_runtime.yaml +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: devbox.sealos.io/v1alpha1 -kind: Runtime -metadata: - name: go-1-22-5 - namespace: devbox-system -spec: - classRef: go - title: go1.22.5 - description: go1.22.5 - config: - image: ghcr.io/cbluebird/devbox/go1.22.5:2f4067 - workingDir: /home/sealos/project - releaseCommand: - - /bin/bash - - -c - releaseArgs: - - /home/sealos/project/entrypoint.sh - category: - - ubuntu - - go ---- -apiVersion: devbox.sealos.io/v1alpha1 -kind: Runtime -metadata: - name: go-1-23-0 - namespace: devbox-system -spec: - classRef: go - config: - image: ghcr.io/labring-actions/devbox/go-1.23.0:409348 - ports: - - containerPort: 22 - name: devbox-ssh-port - protocol: TCP - appPorts: - - name: devbox-app-port - port: 8080 - protocol: TCP - user: sealos - workingDir: /home/sealos/project - releaseCommand: - - /bin/bash - - -c - releaseArgs: - - /home/sealos/project/entrypoint.sh - description: go 1.23.0 - version: "1.23.0" ---- -apiVersion: devbox.sealos.io/v1alpha1 -kind: Runtime -metadata: - name: gin - namespace: devbox-system -spec: - classRef: gin - title: gin - description: gin - config: - image: ghcr.io/cbluebird/devbox/gin:2f4067 - category: - - ubuntu - - go - - gin ---- -apiVersion: devbox.sealos.io/v1alpha1 -kind: Runtime -metadata: - name: spring-boot - namespace: devbox-system -spec: - classRef: spring-boot - title: Spring Boot - description: Spring Boot - config: - image: ghcr.io/cbluebird/devbox/spring-boot:2f4067 - category: - - ubuntu - - java - - spring-boot diff --git a/controllers/devbox/config/samples/devbox_v1alpha1_runtimeclass.yaml b/controllers/devbox/config/samples/devbox_v1alpha1_runtimeclass.yaml deleted file mode 100644 index 3886893f263..00000000000 --- a/controllers/devbox/config/samples/devbox_v1alpha1_runtimeclass.yaml +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright © 2024 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: devbox.sealos.io/v1alpha1 -kind: RuntimeClass -metadata: - name: go - namespace: devbox-system -spec: - kind: Language - title: go - description: go ---- -apiVersion: devbox.sealos.io/v1alpha1 -kind: RuntimeClass -metadata: - name: gin - namespace: devbox-system -spec: - kind: Framework - title: gin - description: gin ---- -apiVersion: devbox.sealos.io/v1alpha1 -kind: RuntimeClass -metadata: - name: java - namespace: devbox-system -spec: - kind: Language - title: java - description: java ---- -apiVersion: devbox.sealos.io/v1alpha1 -kind: RuntimeClass -metadata: - name: spring-boot - namespace: devbox-system -spec: - kind: Framework - title: Spring Boot - description: Spring Boot ---- -apiVersion: devbox.sealos.io/v1alpha1 -kind: RuntimeClass -metadata: - name: python - namespace: devbox-system -spec: - kind: Language - title: python - description: python ---- -apiVersion: devbox.sealos.io/v1alpha1 -kind: RuntimeClass -metadata: - name: nodejs - namespace: devbox-system -spec: - kind: Language - title: node.js - description: node.js \ No newline at end of file diff --git a/controllers/devbox/deploy/manifests/deploy.yaml.tmpl b/controllers/devbox/deploy/manifests/deploy.yaml.tmpl index 7286e29b5b8..910d8310bf1 100644 --- a/controllers/devbox/deploy/manifests/deploy.yaml.tmpl +++ b/controllers/devbox/deploy/manifests/deploy.yaml.tmpl @@ -40,12 +40,6 @@ spec: - jsonPath: .spec.state name: State type: string - - jsonPath: .spec.runtimeRef.name - name: RuntimeRef - type: string - - jsonPath: .status.podPhase - name: PodPhase - type: string - jsonPath: .status.network.type name: NetworkType type: string @@ -842,2488 +836,145 @@ spec: type: array type: object type: object - args: - items: - type: string - type: array - command: - items: - type: string - type: array - extraAnnotations: - additionalProperties: - type: string - type: object - extraEnvs: - description: todo add rewrite env... - items: - description: EnvVar represents an environment variable present in - a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: |- - Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in the container and - any service environment variables. If a variable cannot be resolved, - the reference in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether the variable - exists or not. - Defaults to "". + config: + properties: + annotations: + additionalProperties: type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. + type: object + appPorts: + default: + - name: devbox-app-port + port: 8080 + protocol: TCP + items: + description: ServicePort contains information on service's port. properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: |- - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. - properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: + appProtocol: description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array - extraLabels: - additionalProperties: - type: string - description: todo add rewrite labels and annotations... - type: object - extraVolumeMounts: - items: - description: VolumeMount describes a mounting of a Volume within - a container. - properties: - mountPath: - description: |- - Path within the container at which the volume should be mounted. Must - not contain ':'. - type: string - mountPropagation: - description: |- - mountPropagation determines how mounts are propagated from the host - to container and the other way around. - When not set, MountPropagationNone is used. - This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: |- - Mounted read-only if true, read-write otherwise (false or unspecified). - Defaults to false. - type: boolean - subPath: - description: |- - Path within the volume from which the container's volume should be mounted. - Defaults to "" (volume's root). - type: string - subPathExpr: - description: |- - Expanded path within the volume from which the container's volume should be mounted. - Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. - Defaults to "" (volume's root). - SubPathExpr and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - extraVolumes: - description: todo add rewrite volumes and volume mounts.. - items: - description: Volume represents a named volume in a pod that may - be accessed by any container in the pod. - properties: - awsElasticBlockStore: - description: |- - awsElasticBlockStore represents an AWS Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - properties: - fsType: + The application protocol for this port. + This is used as a hint for implementations to offer richer behavior for protocols that they understand. + This field follows standard Kubernetes label syntax. + Valid values are either: + + + * Un-prefixed protocol names - reserved for IANA standard service names (as per + RFC-6335 and https://www.iana.org/assignments/service-names). + + + * Kubernetes-defined prefixed names: + * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540 + * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455 + * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455 + + + * Other protocols should use implementation-defined prefixed names such as + mycompany.com/my-custom-protocol. + type: string + name: description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from compromising the machine + The name of this port within the service. This must be a DNS_LABEL. + All ports within a ServiceSpec must have unique names. When considering + the endpoints for a Service, this must match the 'name' field in the + EndpointPort. + Optional if only one ServicePort is defined on this service. type: string - partition: + nodePort: description: |- - partition is the partition in the volume that you want to mount. - If omitted, the default is to mount by volume name. - Examples: For volume /dev/sda1, you specify the partition as "1". - Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + The port on each node on which this service is exposed when type is + NodePort or LoadBalancer. Usually assigned by the system. If a value is + specified, in-range, and not in use it will be used, otherwise the + operation will fail. If not specified, a port will be allocated if this + Service requires one. If this field is specified when creating a + Service which does not need it, creation will fail. This field will be + wiped when updating a Service to no longer need it (e.g. changing type + from NodePort to ClusterIP). + More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport format: int32 type: integer - readOnly: - description: |- - readOnly value true will force the readOnly setting in VolumeMounts. - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - type: boolean - volumeID: - description: |- - volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). - More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - type: string - required: - - volumeID - type: object - azureDisk: - description: azureDisk represents an Azure Data Disk mount on - the host and bind mount to the pod. - properties: - cachingMode: - description: 'cachingMode is the Host Caching mode: None, - Read Only, Read Write.' - type: string - diskName: - description: diskName is the Name of the data disk in the - blob storage - type: string - diskURI: - description: diskURI is the URI of data disk in the blob - storage - type: string - fsType: + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP description: |- - fsType is Filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - kind: - description: 'kind expected values are Shared: multiple - blob disks per storage account Dedicated: single blob - disk per storage account Managed: azure managed data - disk (only in managed availability set). defaults to shared' + The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". + Default is TCP. type: string - readOnly: - description: |- - readOnly Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: azureFile represents an Azure File Service mount - on the host and bind mount to the pod. - properties: - readOnly: + targetPort: + anyOf: + - type: integer + - type: string description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretName: - description: secretName is the name of secret that contains - Azure Storage Account Name and Key - type: string - shareName: - description: shareName is the azure share Name - type: string + Number or name of the port to access on the pods targeted by the service. + Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named port in the + target Pod's container ports. If this is not specified, the value + of the 'port' field is used (an identity map). + This field is ignored for services with clusterIP=None, and should be + omitted or set equal to the 'port' field. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service + x-kubernetes-int-or-string: true required: - - secretName - - shareName + - port type: object - cephfs: - description: cephFS represents a Ceph FS mount on the host that - shares a pod's lifetime + type: array + args: + description: kubebuilder:validation:Optional + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + description: EnvVar represents an environment variable present + in a Container. properties: - monitors: - description: |- - monitors is Required: Monitors is a collection of Ceph monitors - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - items: - type: string - type: array - path: - description: 'path is Optional: Used as the mounted root, - rather than the full Ceph tree, default is /' + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. type: string - readOnly: - description: |- - readOnly is Optional: Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - type: boolean - secretFile: + value: description: |- - secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string - secretRef: - description: |- - secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - user: - description: |- - user is optional: User is the rados user name, default is admin - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it - type: string - required: - - monitors - type: object - cinder: - description: |- - cinder represents a cinder volume attached and mounted on kubelets host machine. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: boolean - secretRef: - description: |- - secretRef is optional: points to a secret object containing parameters used to connect - to OpenStack. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - volumeID: - description: |- - volumeID used to identify the volume in cinder. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md - type: string - required: - - volumeID - type: object - configMap: - description: configMap represents a configMap that should populate - this volume - properties: - defaultMode: - description: |- - defaultMode is optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - ConfigMap will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional specify whether the ConfigMap or its - keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - csi: - description: csi (Container Storage Interface) represents ephemeral - storage that is handled by certain external CSI drivers (Beta - feature). - properties: - driver: - description: |- - driver is the name of the CSI driver that handles this volume. - Consult with your admin for the correct name as registered in the cluster. - type: string - fsType: - description: |- - fsType to mount. Ex. "ext4", "xfs", "ntfs". - If not provided, the empty value is passed to the associated CSI driver - which will determine the default filesystem to apply. - type: string - nodePublishSecretRef: - description: |- - nodePublishSecretRef is a reference to the secret object containing - sensitive information to pass to the CSI driver to complete the CSI - NodePublishVolume and NodeUnpublishVolume calls. - This field is optional, and may be empty if no secret is required. If the - secret object contains more than one secret, all secret references are passed. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - readOnly: - description: |- - readOnly specifies a read-only configuration for the volume. - Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: |- - volumeAttributes stores driver-specific properties that are passed to the CSI - driver. Consult your driver's documentation for supported values. - type: object - required: - - driver - type: object - downwardAPI: - description: downwardAPI represents downward API about the pod - that should populate this volume - properties: - defaultMode: - description: |- - Optional: mode bits to use on created files by default. Must be a - Optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: Items is a list of downward API volume file - items: - description: DownwardAPIVolumeFile represents information - to create the file containing the pod field - properties: - fieldRef: - description: 'Required: Selects a field of the pod: - only annotations, labels, name and namespace are - supported.' - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the - specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - description: |- - Optional: mode bits used to set permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: 'Required: Path is the relative path - name of the file to be created. Must not be absolute - or contain the ''..'' path. Must be utf-8 encoded. - The first item of the relative path must not start - with ''..''' - type: string - resourceFieldRef: - description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - type: object - emptyDir: - description: |- - emptyDir represents a temporary directory that shares a pod's lifetime. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - properties: - medium: - description: |- - medium represents what type of storage medium should back this directory. - The default is "" which means to use the node's default medium. - Must be an empty string (default) or Memory. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: |- - sizeLimit is the total amount of local storage required for this EmptyDir volume. - The size limit is also applicable for memory medium. - The maximum usage on memory medium EmptyDir would be the minimum value between - the SizeLimit specified here and the sum of memory limits of all containers in a pod. - The default is nil which means that the limit is undefined. - More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: |- - ephemeral represents a volume that is handled by a cluster storage driver. - The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, - and deleted when the pod is removed. - - - Use this if: - a) the volume is only needed while the pod runs, - b) features of normal volumes like restoring from snapshot or capacity - tracking are needed, - c) the storage driver is specified through a storage class, and - d) the storage driver supports dynamic volume provisioning through - a PersistentVolumeClaim (see EphemeralVolumeSource for more - information on the connection between this volume type - and PersistentVolumeClaim). - - - Use PersistentVolumeClaim or one of the vendor-specific - APIs for volumes that persist for longer than the lifecycle - of an individual pod. - - - Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to - be used that way - see the documentation of the driver for - more information. - - - A pod can use both types of ephemeral volumes and - persistent volumes at the same time. - properties: - volumeClaimTemplate: - description: |- - Will be used to create a stand-alone PVC to provision the volume. - The pod in which this EphemeralVolumeSource is embedded will be the - owner of the PVC, i.e. the PVC will be deleted together with the - pod. The name of the PVC will be `-` where - `` is the name from the `PodSpec.Volumes` array - entry. Pod validation will reject the pod if the concatenated name - is not valid for a PVC (for example, too long). - - - An existing PVC with that name that is not owned by the pod - will *not* be used for the pod to avoid using an unrelated - volume by mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created PVC is - meant to be used by the pod, the PVC has to updated with an - owner reference to the pod once the pod exists. Normally - this should not be necessary, but it may be useful when - manually reconstructing a broken cluster. - - - This field is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. - - - Required, must not be nil. - properties: - metadata: - description: |- - May contain labels and annotations that will be copied into the PVC - when creating it. No other fields are allowed and will be rejected during - validation. - type: object - spec: - description: |- - The specification for the PersistentVolumeClaim. The entire content is - copied unchanged into the PVC that gets created from this - template. The same fields as in a PersistentVolumeClaim - are also valid here. - properties: - accessModes: - description: |- - accessModes contains the desired access modes the volume should have. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 - items: - type: string - type: array - dataSource: - description: |- - dataSource field can be used to specify either: - * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) - If the provisioner or an external controller can support the specified data source, - it will create a new volume based on the contents of the specified data source. - When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, - and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. - If the namespace is specified, then dataSourceRef will not be copied to dataSource. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - description: |- - dataSourceRef specifies the object from which to populate the volume with data, if a non-empty - volume is desired. This may be any object from a non-empty API group (non - core object) or a PersistentVolumeClaim object. - When this field is specified, volume binding will only succeed if the type of - the specified object matches some installed volume populator or dynamic - provisioner. - This field will replace the functionality of the dataSource field and as such - if both fields are non-empty, they must have the same value. For backwards - compatibility, when namespace isn't specified in dataSourceRef, - both fields (dataSource and dataSourceRef) will be set to the same - value automatically if one of them is empty and the other is non-empty. - When namespace is specified in dataSourceRef, - dataSource isn't set to the same value and must be empty. - There are three important differences between dataSource and dataSourceRef: - * While dataSource only allows two specific types of objects, dataSourceRef - allows any non-core object, as well as PersistentVolumeClaim objects. - * While dataSource ignores disallowed values (dropping them), dataSourceRef - preserves all values, and generates an error if a disallowed value is - specified. - * While dataSource only allows local objects, dataSourceRef allows objects - in any namespaces. - (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. - (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - namespace: - description: |- - Namespace is the namespace of resource being referenced - Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: |- - resources represents the minimum resources the volume should have. - If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements - that are lower than previous value but must still be higher than capacity recorded in the - status field of the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources - properties: - claims: - description: |- - Claims lists the names of resources, defined in spec.resourceClaims, - that are used by this container. - - - This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. - - - This field is immutable. It can only be set for containers. - items: - description: ResourceClaim references one - entry in PodSpec.ResourceClaims. - properties: - name: - description: |- - Name must match the name of one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Limits describes the maximum amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Requests describes the minimum amount of compute resources required. - If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - type: object - selector: - description: selector is a label query over volumes - to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: |- - storageClassName is the name of the StorageClass required by the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 - type: string - volumeMode: - description: |- - volumeMode defines what type of volume is required by the claim. - Value of Filesystem is implied when not included in claim spec. - type: string - volumeName: - description: volumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - fc: - description: fc represents a Fibre Channel resource that is - attached to a kubelet's host machine and then exposed to the - pod. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - lun: - description: 'lun is Optional: FC target lun number' - format: int32 - type: integer - readOnly: - description: |- - readOnly is Optional: Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - targetWWNs: - description: 'targetWWNs is Optional: FC target worldwide - names (WWNs)' - items: - type: string - type: array - wwids: - description: |- - wwids Optional: FC volume world wide identifiers (wwids) - Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. - items: - type: string - type: array - type: object - flexVolume: - description: |- - flexVolume represents a generic volume resource that is - provisioned/attached using an exec based plugin. - properties: - driver: - description: driver is the name of the driver to use for - this volume. - type: string - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'options is Optional: this field holds extra - command options if any.' - type: object - readOnly: - description: |- - readOnly is Optional: defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef is Optional: secretRef is reference to the secret object containing - sensitive information to pass to the plugin scripts. This may be - empty if no secret object is specified. If the secret object - contains more than one secret, all secrets are passed to the plugin - scripts. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - required: - - driver - type: object - flocker: - description: flocker represents a Flocker volume attached to - a kubelet's host machine. This depends on the Flocker control - service being running - properties: - datasetName: - description: |- - datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker - should be considered as deprecated - type: string - datasetUUID: - description: datasetUUID is the UUID of the dataset. This - is unique identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: |- - gcePersistentDisk represents a GCE Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - properties: - fsType: - description: |- - fsType is filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - partition: - description: |- - partition is the partition in the volume that you want to mount. - If omitted, the default is to mount by volume name. - Examples: For volume /dev/sda1, you specify the partition as "1". - Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - format: int32 - type: integer - pdName: - description: |- - pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - type: string - readOnly: - description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - type: boolean - required: - - pdName - type: object - gitRepo: - description: |- - gitRepo represents a git repository at a particular revision. - DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an - EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir - into the Pod's container. - properties: - directory: - description: |- - directory is the target directory name. - Must not contain or start with '..'. If '.' is supplied, the volume directory will be the - git repository. Otherwise, if specified, the volume will contain the git repository in - the subdirectory with the given name. - type: string - repository: - description: repository is the URL - type: string - revision: - description: revision is the commit hash for the specified - revision. - type: string - required: - - repository - type: object - glusterfs: - description: |- - glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. - More info: https://examples.k8s.io/volumes/glusterfs/README.md - properties: - endpoints: - description: |- - endpoints is the endpoint name that details Glusterfs topology. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: string - path: - description: |- - path is the Glusterfs volume path. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: string - readOnly: - description: |- - readOnly here will force the Glusterfs volume to be mounted with read-only permissions. - Defaults to false. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: |- - hostPath represents a pre-existing file or directory on the host - machine that is directly exposed to the container. This is generally - used for system agents or other privileged things that are allowed - to see the host machine. Most containers will NOT need this. - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- - TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not - mount host directories as read/write. - properties: - path: - description: |- - path of the directory on the host. - If the path is a symlink, it will follow the link to the real path. - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - type: string - type: - description: |- - type for HostPath Volume - Defaults to "" - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - type: string - required: - - path - type: object - iscsi: - description: |- - iscsi represents an ISCSI Disk resource that is attached to a - kubelet's host machine and then exposed to the pod. - More info: https://examples.k8s.io/volumes/iscsi/README.md - properties: - chapAuthDiscovery: - description: chapAuthDiscovery defines whether support iSCSI - Discovery CHAP authentication - type: boolean - chapAuthSession: - description: chapAuthSession defines whether support iSCSI - Session CHAP authentication - type: boolean - fsType: - description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - initiatorName: - description: |- - initiatorName is the custom iSCSI Initiator Name. - If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface - : will be created for the connection. - type: string - iqn: - description: iqn is the target iSCSI Qualified Name. - type: string - iscsiInterface: - description: |- - iscsiInterface is the interface Name that uses an iSCSI transport. - Defaults to 'default' (tcp). - type: string - lun: - description: lun represents iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: |- - portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. - type: boolean - secretRef: - description: secretRef is the CHAP Secret for iSCSI target - and initiator authentication - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - targetPortal: - description: |- - targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: |- - name of the volume. - Must be a DNS_LABEL and unique within the pod. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - nfs: - description: |- - nfs represents an NFS mount on the host that shares a pod's lifetime - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - properties: - path: - description: |- - path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - type: string - readOnly: - description: |- - readOnly here will force the NFS export to be mounted with read-only permissions. - Defaults to false. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - type: boolean - server: - description: |- - server is the hostname or IP address of the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: |- - persistentVolumeClaimVolumeSource represents a reference to a - PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims - properties: - claimName: - description: |- - claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims - type: string - readOnly: - description: |- - readOnly Will force the ReadOnly setting in VolumeMounts. - Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: photonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host machine - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - pdID: - description: pdID is the ID that identifies Photon Controller - persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: portworxVolume represents a portworx volume attached - and mounted on kubelets host machine - properties: - fsType: - description: |- - fSType represents the filesystem type to mount - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - volumeID: - description: volumeID uniquely identifies a Portworx volume - type: string - required: - - volumeID - type: object - projected: - description: projected items for all in one resources secrets, - configmaps, and downward API - properties: - defaultMode: - description: |- - defaultMode are the mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - sources: - description: sources is the list of volume projections - items: - description: Projection that may be projected along with - other supported volume types - properties: - configMap: - description: configMap information about the configMap - data to project - properties: - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - ConfigMap will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the ConfigMap, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional specify whether the ConfigMap - or its keys must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - downwardAPI: - description: downwardAPI information about the downwardAPI - data to project - properties: - items: - description: Items is a list of DownwardAPIVolume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects a field - of the pod: only annotations, labels, - name and namespace are supported.' - properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, - defaults to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - mode: - description: |- - Optional: mode bits used to set permissions on this file, must be an octal value - between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: 'Required: Path is the relative - path name of the file to be created. Must - not be absolute or contain the ''..'' - path. Must be utf-8 encoded. The first - item of the relative path must not start - with ''..''' - type: string - resourceFieldRef: - description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. - properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults - to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to - select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - required: - - path - type: object - type: array - type: object - secret: - description: secret information about the secret data - to project - properties: - items: - description: |- - items if unspecified, each key-value pair in the Data field of the referenced - Secret will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the Secret, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: optional field specify whether the - Secret or its key must be defined - type: boolean - type: object - x-kubernetes-map-type: atomic - serviceAccountToken: - description: serviceAccountToken is information about - the serviceAccountToken data to project - properties: - audience: - description: |- - audience is the intended audience of the token. A recipient of a token - must identify itself with an identifier specified in the audience of the - token, and otherwise should reject the token. The audience defaults to the - identifier of the apiserver. - type: string - expirationSeconds: - description: |- - expirationSeconds is the requested duration of validity of the service - account token. As the token approaches expiration, the kubelet volume - plugin will proactively rotate the service account token. The kubelet will - start trying to rotate the token if the token is older than 80 percent of - its time to live or if the token is older than 24 hours.Defaults to 1 hour - and must be at least 10 minutes. - format: int64 - type: integer - path: - description: |- - path is the path relative to the mount point of the file to project the - token into. - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - description: quobyte represents a Quobyte mount on the host - that shares a pod's lifetime - properties: - group: - description: |- - group to map volume access to - Default is no group - type: string - readOnly: - description: |- - readOnly here will force the Quobyte volume to be mounted with read-only permissions. - Defaults to false. - type: boolean - registry: - description: |- - registry represents a single or multiple Quobyte Registry services - specified as a string as host:port pair (multiple entries are separated with commas) - which acts as the central registry for volumes - type: string - tenant: - description: |- - tenant owning the given Quobyte volume in the Backend - Used with dynamically provisioned Quobyte volumes, value is set by the plugin - type: string - user: - description: |- - user to map volume access to - Defaults to serivceaccount user - type: string - volume: - description: volume is a string that references an already - created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: |- - rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. - More info: https://examples.k8s.io/volumes/rbd/README.md - properties: - fsType: - description: |- - fsType is the filesystem type of the volume that you want to mount. - Tip: Ensure that the filesystem type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from compromising the machine - type: string - image: - description: |- - image is the rados image name. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - keyring: - description: |- - keyring is the path to key ring for RBDUser. - Default is /etc/ceph/keyring. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - monitors: - description: |- - monitors is a collection of Ceph monitors. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - items: - type: string - type: array - pool: - description: |- - pool is the rados pool name. - Default is rbd. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - readOnly: - description: |- - readOnly here will force the ReadOnly setting in VolumeMounts. - Defaults to false. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: boolean - secretRef: - description: |- - secretRef is name of the authentication secret for RBDUser. If provided - overrides keyring. - Default is nil. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - user: - description: |- - user is the rados user name. - Default is admin. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it - type: string - required: - - image - - monitors - type: object - scaleIO: - description: scaleIO represents a ScaleIO persistent volume - attached and mounted on Kubernetes nodes. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". - Default is "xfs". - type: string - gateway: - description: gateway is the host address of the ScaleIO - API Gateway. - type: string - protectionDomain: - description: protectionDomain is the name of the ScaleIO - Protection Domain for the configured storage. - type: string - readOnly: - description: |- - readOnly Defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef references to the secret for ScaleIO user and other - sensitive information. If this is not provided, Login operation will fail. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - sslEnabled: - description: sslEnabled Flag enable/disable SSL communication - with Gateway, default false - type: boolean - storageMode: - description: |- - storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. - Default is ThinProvisioned. - type: string - storagePool: - description: storagePool is the ScaleIO Storage Pool associated - with the protection domain. - type: string - system: - description: system is the name of the storage system as - configured in ScaleIO. - type: string - volumeName: - description: |- - volumeName is the name of a volume already created in the ScaleIO system - that is associated with this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: |- - secret represents a secret that should populate this volume. - More info: https://kubernetes.io/docs/concepts/storage/volumes#secret - properties: - defaultMode: - description: |- - defaultMode is Optional: mode bits used to set permissions on created files by default. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values - for mode bits. Defaults to 0644. - Directories within the path are not affected by this setting. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - items: - description: |- - items If unspecified, each key-value pair in the Data field of the referenced - Secret will be projected into the volume as a file whose name is the - key and content is the value. If specified, the listed keys will be - projected into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in the Secret, - the volume setup will error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: key is the key to project. - type: string - mode: - description: |- - mode is Optional: mode bits used to set permissions on this file. - Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. - YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. - If not specified, the volume defaultMode will be used. - This might be in conflict with other options that affect the file - mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - path: - description: |- - path is the relative path of the file to map the key to. - May not be an absolute path. - May not contain the path element '..'. - May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: optional field specify whether the Secret or - its keys must be defined - type: boolean - secretName: - description: |- - secretName is the name of the secret in the pod's namespace to use. - More info: https://kubernetes.io/docs/concepts/storage/volumes#secret - type: string - type: object - storageos: - description: storageOS represents a StorageOS volume attached - and mounted on Kubernetes nodes. - properties: - fsType: - description: |- - fsType is the filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: |- - readOnly defaults to false (read/write). ReadOnly here will force - the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: |- - secretRef specifies the secret to use for obtaining the StorageOS API - credentials. If not specified, default values will be attempted. - properties: - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - type: object - x-kubernetes-map-type: atomic - volumeName: - description: |- - volumeName is the human-readable name of the StorageOS volume. Volume - names are only unique within a namespace. - type: string - volumeNamespace: - description: |- - volumeNamespace specifies the scope of the volume within StorageOS. If no - namespace is specified then the Pod's namespace will be used. This allows the - Kubernetes name scoping to be mirrored within StorageOS for tighter integration. - Set VolumeName to any name to override the default behaviour. - Set to "default" if you are not using namespaces within StorageOS. - Namespaces that do not pre-exist within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: vsphereVolume represents a vSphere volume attached - and mounted on kubelets host machine - properties: - fsType: - description: |- - fsType is filesystem type to mount. - Must be a filesystem type supported by the host operating system. - Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - storagePolicyID: - description: storagePolicyID is the storage Policy Based - Management (SPBM) profile ID associated with the StoragePolicyName. - type: string - storagePolicyName: - description: storagePolicyName is the storage Policy Based - Management (SPBM) profile name. - type: string - volumePath: - description: volumePath is the path that identifies vSphere - volume vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - network: - properties: - extraPorts: - items: - description: ContainerPort represents a network port in a single - container. - properties: - containerPort: - description: |- - Number of port to expose on the pod's IP address. - This must be a valid port number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external port to. - type: string - hostPort: - description: |- - Number of port to expose on the host. - If specified, this must be a valid port number, 0 < x < 65536. - If HostNetwork is specified, this must match ContainerPort. - Most containers do not need this. - format: int32 - type: integer - name: - description: |- - If specified, this must be an IANA_SVC_NAME and unique within the pod. Each - named port in a pod must have a unique name. Name for the port that can be - referred to by services. - type: string - protocol: - default: TCP - description: |- - Protocol for port. Must be UDP, TCP, or SCTP. - Defaults to "TCP". - type: string - required: - - containerPort - type: object - type: array - type: - enum: - - NodePort - - Tailnet - type: string - required: - - type - type: object - resource: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - runtimeRef: - properties: - name: - type: string - namespace: - type: string - required: - - name - type: object - squash: - default: false - type: boolean - state: - enum: - - Running - - Stopped - type: string - tolerations: - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - type: object - type: array - workingDir: - type: string - required: - - resource - - runtimeRef - - state - type: object - status: - description: DevboxStatus defines the observed state of Devbox - properties: - commitHistory: - items: - properties: - containerID: - description: ContainerID is the container id - type: string - image: - description: Image is the image of the commit - type: string - node: - description: Node is the node name - type: string - pod: - description: Pod is the pod name - type: string - predicatedStatus: - description: predicatedStatus default `pending`, will be set - to `success` if pod status is running successfully. - type: string - status: - description: status will be set based on expectedStatus after - devbox pod delete or stop. if expectedStatus is still pending, - it means the pod is not running successfully, so we need to - set it to `failed` - type: string - time: - description: Time is the time when the commit is created - format: date-time - type: string - required: - - containerID - - image - - node - - pod - - predicatedStatus - - status - - time - type: object - type: array - lastState: - description: |- - ContainerState holds a possible state of container. - Only one of its members may be specified. - If none of them is specified, the default one is ContainerStateWaiting. - properties: - running: - description: Details about a running container - properties: - startedAt: - description: Time at which the container was last (re-)started - format: date-time - type: string - type: object - terminated: - description: Details about a terminated container - properties: - containerID: - description: Container's ID in the format '://' - type: string - exitCode: - description: Exit status from the last termination of the - container - format: int32 - type: integer - finishedAt: - description: Time at which the container last terminated - format: date-time - type: string - message: - description: Message regarding the last termination of the - container - type: string - reason: - description: (brief) reason from the last termination of the - container - type: string - signal: - description: Signal from the last termination of the container - format: int32 - type: integer - startedAt: - description: Time at which previous execution of the container - started - format: date-time - type: string - required: - - exitCode - type: object - waiting: - description: Details about a waiting container - properties: - message: - description: Message regarding why the container is not yet - running. - type: string - reason: - description: (brief) reason the container is not yet running. - type: string - type: object - type: object - network: - properties: - nodePort: - format: int32 - type: integer - tailnet: - description: todo TailNet - type: string - type: - default: NodePort - enum: - - NodePort - - Tailnet - type: string - required: - - type - type: object - phase: - type: string - state: - description: |- - ContainerState holds a possible state of container. - Only one of its members may be specified. - If none of them is specified, the default one is ContainerStateWaiting. - properties: - running: - description: Details about a running container - properties: - startedAt: - description: Time at which the container was last (re-)started - format: date-time - type: string - type: object - terminated: - description: Details about a terminated container - properties: - containerID: - description: Container's ID in the format '://' - type: string - exitCode: - description: Exit status from the last termination of the - container - format: int32 - type: integer - finishedAt: - description: Time at which the container last terminated - format: date-time - type: string - message: - description: Message regarding the last termination of the - container - type: string - reason: - description: (brief) reason from the last termination of the - container - type: string - signal: - description: Signal from the last termination of the container - format: int32 - type: integer - startedAt: - description: Time at which previous execution of the container - started - format: date-time - type: string - required: - - exitCode - type: object - waiting: - description: Details about a waiting container - properties: - message: - description: Message regarding why the container is not yet - running. - type: string - reason: - description: (brief) reason the container is not yet running. - type: string - type: object - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: devboxreleases.devbox.sealos.io -spec: - group: devbox.sealos.io - names: - kind: DevBoxRelease - listKind: DevBoxReleaseList - plural: devboxreleases - singular: devboxrelease - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.devboxName - name: DevboxName - type: string - - jsonPath: .spec.newTag - name: NewTag - type: string - - jsonPath: .status.phase - name: Phase - type: string - - jsonPath: .status.originalImage - name: OriginalImage - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: DevBoxRelease is the Schema for the devboxreleases API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: DevBoxReleaseSpec defines the desired state of DevBoxRelease - properties: - devboxName: - type: string - newTag: - type: string - notes: - type: string - required: - - devboxName - - newTag - type: object - status: - description: DevBoxReleaseStatus defines the observed state of DevBoxRelease - properties: - originalImage: - type: string - phase: - default: Pending - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: operationrequests.devbox.sealos.io -spec: - group: devbox.sealos.io - names: - kind: OperationRequest - listKind: OperationRequestList - plural: operationrequests - singular: operationrequest - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: OperationRequest is the Schema for the operationrequests API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: OperationRequestSpec defines the desired state of OperationRequest - type: object - status: - description: OperationRequestStatus defines the observed state of OperationRequest - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: runtimeclasses.devbox.sealos.io -spec: - group: devbox.sealos.io - names: - kind: RuntimeClass - listKind: RuntimeClassList - plural: runtimeclasses - singular: runtimeclass - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: RuntimeClass is the Schema for the runtimeclasses API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: RuntimeClassSpec defines the desired state of RuntimeClass - properties: - description: - type: string - kind: - enum: - - OS - - Language - - Framework - type: string - title: - type: string - required: - - kind - - title - type: object - status: - description: RuntimeClassStatus defines the observed state of RuntimeClass - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - name: runtimes.devbox.sealos.io -spec: - group: devbox.sealos.io - names: - kind: Runtime - listKind: RuntimeList - plural: runtimes - singular: runtime - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.classRef - name: Class - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.runtimeVersion - name: RuntimeVersion - type: string - - jsonPath: .spec.state - name: State - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Runtime is the Schema for the runtimes API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: RuntimeSpec defines the desired state of Runtime - properties: - category: - items: - type: string - type: array - classRef: - type: string - components: - items: - properties: - name: - type: string - version: - type: string - required: - - name - - version - type: object - type: array - config: - properties: - annotations: - additionalProperties: - type: string - type: object - appPorts: - default: - - name: devbox-app-port - port: 8080 - protocol: TCP - items: - description: ServicePort contains information on service's port. - properties: - appProtocol: - description: |- - The application protocol for this port. - This is used as a hint for implementations to offer richer behavior for protocols that they understand. - This field follows standard Kubernetes label syntax. - Valid values are either: - - - * Un-prefixed protocol names - reserved for IANA standard service names (as per - RFC-6335 and https://www.iana.org/assignments/service-names). - - - * Kubernetes-defined prefixed names: - * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540 - * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455 - * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455 - - - * Other protocols should use implementation-defined prefixed names such as - mycompany.com/my-custom-protocol. - type: string - name: - description: |- - The name of this port within the service. This must be a DNS_LABEL. - All ports within a ServiceSpec must have unique names. When considering - the endpoints for a Service, this must match the 'name' field in the - EndpointPort. - Optional if only one ServicePort is defined on this service. - type: string - nodePort: - description: |- - The port on each node on which this service is exposed when type is - NodePort or LoadBalancer. Usually assigned by the system. If a value is - specified, in-range, and not in use it will be used, otherwise the - operation will fail. If not specified, a port will be allocated if this - Service requires one. If this field is specified when creating a - Service which does not need it, creation will fail. This field will be - wiped when updating a Service to no longer need it (e.g. changing type - from NodePort to ClusterIP). - More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport - format: int32 - type: integer - port: - description: The port that will be exposed by this service. - format: int32 - type: integer - protocol: - default: TCP - description: |- - The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". - Default is TCP. - type: string - targetPort: - anyOf: - - type: integer - - type: string - description: |- - Number or name of the port to access on the pods targeted by the service. - Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. - If this is a string, it will be looked up as a named port in the - target Pod's container ports. If this is not specified, the value - of the 'port' field is used (an identity map). - This field is ignored for services with clusterIP=None, and should be - omitted or set equal to the 'port' field. - More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - args: - description: kubebuilder:validation:Optional - items: - type: string - type: array - command: - items: - type: string - type: array - env: - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: Name of the environment variable. Must be a - C_IDENTIFIER. - type: string - value: - description: |- - Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in the container and - any service environment variables. If a variable cannot be resolved, - the reference in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether the variable - exists or not. - Defaults to "". - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: description: |- Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. @@ -3391,8 +1042,6 @@ spec: - name type: object type: array - image: - type: string labels: additionalProperties: type: string @@ -3443,7 +1092,7 @@ spec: type: array releaseArgs: default: - - /home/sealos/project/entrypoint.sh + - /home/devbox/project/entrypoint.sh items: type: string type: array @@ -3455,7 +1104,7 @@ spec: type: string type: array user: - default: sealos + default: devbox type: string volumeMounts: items: @@ -5109,30 +2758,393 @@ spec: type: object type: array workingDir: - default: /home/sealos/project + default: /home/devbox/project + type: string + type: object + image: + type: string + network: + properties: + type: + enum: + - NodePort + - Tailnet + type: string + required: + - type + type: object + resource: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + squash: + default: false + type: boolean + state: + enum: + - Running + - Stopped + type: string + templateID: + type: string + tolerations: + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + required: + - config + - image + - resource + - state + type: object + status: + description: DevboxStatus defines the observed state of Devbox + properties: + commitHistory: + items: + properties: + containerID: + description: ContainerID is the container id + type: string + image: + description: Image is the image of the commit + type: string + node: + description: Node is the node name + type: string + pod: + description: Pod is the pod name + type: string + predicatedStatus: + description: predicatedStatus default `pending`, will be set + to `success` if pod status is running successfully. + type: string + status: + description: status will be set based on expectedStatus after + devbox pod delete or stop. if expectedStatus is still pending, + it means the pod is not running successfully, so we need to + set it to `failed` + type: string + time: + description: Time is the time when the commit is created + format: date-time + type: string + required: + - containerID + - image + - node + - pod + - predicatedStatus + - status + - time + type: object + type: array + lastState: + description: |- + ContainerState holds a possible state of container. + Only one of its members may be specified. + If none of them is specified, the default one is ContainerStateWaiting. + properties: + running: + description: Details about a running container + properties: + startedAt: + description: Time at which the container was last (re-)started + format: date-time + type: string + type: object + terminated: + description: Details about a terminated container + properties: + containerID: + description: Container's ID in the format '://' + type: string + exitCode: + description: Exit status from the last termination of the + container + format: int32 + type: integer + finishedAt: + description: Time at which the container last terminated + format: date-time + type: string + message: + description: Message regarding the last termination of the + container + type: string + reason: + description: (brief) reason from the last termination of the + container + type: string + signal: + description: Signal from the last termination of the container + format: int32 + type: integer + startedAt: + description: Time at which previous execution of the container + started + format: date-time + type: string + required: + - exitCode + type: object + waiting: + description: Details about a waiting container + properties: + message: + description: Message regarding why the container is not yet + running. + type: string + reason: + description: (brief) reason the container is not yet running. + type: string + type: object + type: object + network: + properties: + nodePort: + format: int32 + type: integer + tailnet: + description: todo TailNet + type: string + type: + default: NodePort + enum: + - NodePort + - Tailnet type: string required: - - image + - type + type: object + phase: + type: string + state: + description: |- + ContainerState holds a possible state of container. + Only one of its members may be specified. + If none of them is specified, the default one is ContainerStateWaiting. + properties: + running: + description: Details about a running container + properties: + startedAt: + description: Time at which the container was last (re-)started + format: date-time + type: string + type: object + terminated: + description: Details about a terminated container + properties: + containerID: + description: Container's ID in the format '://' + type: string + exitCode: + description: Exit status from the last termination of the + container + format: int32 + type: integer + finishedAt: + description: Time at which the container last terminated + format: date-time + type: string + message: + description: Message regarding the last termination of the + container + type: string + reason: + description: (brief) reason from the last termination of the + container + type: string + signal: + description: Signal from the last termination of the container + format: int32 + type: integer + startedAt: + description: Time at which previous execution of the container + started + format: date-time + type: string + required: + - exitCode + type: object + waiting: + description: Details about a waiting container + properties: + message: + description: Message regarding why the container is not yet + running. + type: string + reason: + description: (brief) reason the container is not yet running. + type: string + type: object type: object - description: - type: string - runtimeVersion: + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: devboxreleases.devbox.sealos.io +spec: + group: devbox.sealos.io + names: + kind: DevBoxRelease + listKind: DevBoxReleaseList + plural: devboxreleases + singular: devboxrelease + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.devboxName + name: DevboxName + type: string + - jsonPath: .spec.newTag + name: NewTag + type: string + - jsonPath: .status.phase + name: Phase + type: string + - jsonPath: .status.originalImage + name: OriginalImage + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: DevBoxRelease is the Schema for the devboxreleases API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: DevBoxReleaseSpec defines the desired state of DevBoxRelease + properties: + devboxName: type: string - state: - default: active - enum: - - active - - deprecated + newTag: type: string - version: + notes: type: string required: - - classRef - - config - - version + - devboxName + - newTag + type: object + status: + description: DevBoxReleaseStatus defines the observed state of DevBoxRelease + properties: + originalImage: + type: string + phase: + default: Pending + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: operationrequests.devbox.sealos.io +spec: + group: devbox.sealos.io + names: + kind: OperationRequest + listKind: OperationRequestList + plural: operationrequests + singular: operationrequest + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: OperationRequest is the Schema for the operationrequests API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: OperationRequestSpec defines the desired state of OperationRequest type: object status: - description: RuntimeStatus defines the observed state of Runtime + description: OperationRequestStatus defines the observed state of OperationRequest type: object type: object served: true @@ -5483,106 +3495,6 @@ rules: - get --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: devbox - name: devbox-runtime-editor-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - runtimes - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimes/status - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: devbox - name: devbox-runtime-viewer-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - runtimes - verbs: - - get - - list - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimes/status - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: devbox - name: devbox-runtimeclass-editor-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses/status - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: devbox - name: devbox-runtimeclass-viewer-role -rules: -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses - verbs: - - get - - list - - watch -- apiGroups: - - devbox.sealos.io - resources: - - runtimeclasses/status - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: @@ -5675,7 +3587,6 @@ spec: - --registry-addr={{ .registryAddr }} - --registry-user={{ .registryUser }} - --registry-password={{ .registryPassword }} - - --auth-addr={{ .authAddr }} command: - /manager image: ghcr.io/labring/sealos-devbox-controller:latest diff --git a/controllers/devbox/internal/controller/devbox_controller.go b/controllers/devbox/internal/controller/devbox_controller.go index 76fcb82d9da..65fe7e292ef 100644 --- a/controllers/devbox/internal/controller/devbox_controller.go +++ b/controllers/devbox/internal/controller/devbox_controller.go @@ -23,6 +23,8 @@ import ( devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" "github.com/labring/sealos/controllers/devbox/internal/controller/helper" + "github.com/labring/sealos/controllers/devbox/internal/controller/utils/matcher" + "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" "github.com/labring/sealos/controllers/devbox/label" corev1 "k8s.io/api/core/v1" @@ -44,11 +46,12 @@ import ( // DevboxReconciler reconciles a Devbox object type DevboxReconciler struct { - CommitImageRegistry string - RequestCPURate float64 - RequestMemoryRate float64 - RequestEphemeralStorage string - LimitEphemeralStorage string + CommitImageRegistry string + + RequestRate resource.RequestRate + EphemeralStorage resource.EphemeralStorage + + PodMatchers []matcher.PodMatcher DebugMode bool @@ -260,12 +263,8 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.D switch devbox.Spec.State { case devboxv1alpha1.DevboxStateRunning: - runtimecr, err := r.getRuntime(ctx, devbox) - if err != nil { - return err - } nextCommitHistory := r.generateNextCommitHistory(devbox) - expectPod := r.generateDevboxPod(devbox, runtimecr, nextCommitHistory) + expectPod := r.generateDevboxPod(devbox, nextCommitHistory) switch len(podList.Items) { case 0: @@ -298,7 +297,7 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.D logger.Info("pod has been deleted") return r.handlePodDeleted(ctx, devbox, pod) } - switch helper.PodMatchExpectations(expectPod, pod) { + switch matcher.PodMatchExpectations(expectPod, pod, r.PodMatchers...) { case true: // pod match expectations logger.Info("pod match expectations") @@ -343,12 +342,8 @@ func (r *DevboxReconciler) syncPod(ctx context.Context, devbox *devboxv1alpha1.D } func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error { - runtimecr, err := r.getRuntime(ctx, devbox) - if err != nil { - return err - } var servicePorts []corev1.ServicePort - for _, port := range runtimecr.Spec.Config.Ports { + for _, port := range devbox.Spec.Config.Ports { servicePorts = append(servicePorts, corev1.ServicePort{ Name: port.Name, Port: port.ContainerPort, @@ -399,7 +394,7 @@ func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alph // Retrieve the updated Service to get the NodePort var updatedService corev1.Service - err = retry.OnError( + err := retry.OnError( retry.DefaultRetry, func(err error) bool { return client.IgnoreNotFound(err) == nil }, func() error { @@ -426,26 +421,27 @@ func (r *DevboxReconciler) syncService(ctx context.Context, devbox *devboxv1alph return r.Status().Update(ctx, devbox) } -// get the runtime -func (r *DevboxReconciler) getRuntime(ctx context.Context, devbox *devboxv1alpha1.Devbox) (*devboxv1alpha1.Runtime, error) { - runtimeNamespace := devbox.Spec.RuntimeRef.Namespace - if runtimeNamespace == "" { - runtimeNamespace = devbox.Namespace - } - runtimecr := &devboxv1alpha1.Runtime{} - if err := r.Get(ctx, client.ObjectKey{Namespace: runtimeNamespace, Name: devbox.Spec.RuntimeRef.Name}, runtimecr); err != nil { - return nil, err - } - return runtimecr, nil -} - // create a new pod, add predicated status to nextCommitHistory func (r *DevboxReconciler) createPod(ctx context.Context, devbox *devboxv1alpha1.Devbox, expectPod *corev1.Pod, nextCommitHistory *devboxv1alpha1.CommitHistory) error { + logger := log.FromContext(ctx) + + logger.Info("creating pod", + "podName", expectPod.Name, + "namespace", expectPod.Namespace, + "nextCommitHistory", nextCommitHistory) + nextCommitHistory.Status = devboxv1alpha1.CommitStatusPending nextCommitHistory.PredicatedStatus = devboxv1alpha1.CommitStatusPending + + if expectPod.Name == "" { + return fmt.Errorf("pod name cannot be empty") + } + if err := r.Create(ctx, expectPod); err != nil { + logger.Error(err, "failed to create pod") return err } + devbox.Status.CommitHistory = append(devbox.Status.CommitHistory, nextCommitHistory) return nil } @@ -513,38 +509,34 @@ func (r *DevboxReconciler) deleteResourcesByLabels(ctx context.Context, obj clie return client.IgnoreNotFound(err) } -func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, runtime *devboxv1alpha1.Runtime, nextCommitHistory *devboxv1alpha1.CommitHistory) *corev1.Pod { +func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, nextCommitHistory *devboxv1alpha1.CommitHistory) *corev1.Pod { objectMeta := metav1.ObjectMeta{ Name: nextCommitHistory.Pod, Namespace: devbox.Namespace, - Labels: helper.GeneratePodLabels(devbox, runtime), - Annotations: helper.GeneratePodAnnotations(devbox, runtime), + Labels: helper.GeneratePodLabels(devbox), + Annotations: helper.GeneratePodAnnotations(devbox), } - // set up ports and env by using runtime ports and devbox extra ports - ports := runtime.Spec.Config.Ports + ports := devbox.Spec.Config.Ports // TODO: add extra ports to pod, currently not support // ports = append(ports, devbox.Spec.NetworkSpec.ExtraPorts...) - envs := runtime.Spec.Config.Env - envs = append(envs, devbox.Spec.ExtraEnvs...) + envs := devbox.Spec.Config.Env envs = append(envs, helper.GenerateDevboxEnvVars(devbox, nextCommitHistory)...) //get image name var imageName string if r.DebugMode { - imageName = runtime.Spec.Config.Image + imageName = devbox.Spec.Image } else { - imageName = helper.GetLastSuccessCommitImageName(devbox, runtime) + imageName = helper.GetLastSuccessCommitImageName(devbox) } - volumes := runtime.Spec.Config.Volumes + volumes := devbox.Spec.Config.Volumes volumes = append(volumes, helper.GenerateSSHVolume(devbox)) - volumes = append(volumes, devbox.Spec.ExtraVolumes...) - volumeMounts := runtime.Spec.Config.VolumeMounts + volumeMounts := devbox.Spec.Config.VolumeMounts volumeMounts = append(volumeMounts, helper.GenerateSSHVolumeMounts()...) - volumeMounts = append(volumeMounts, devbox.Spec.ExtraVolumeMounts...) containers := []corev1.Container{ { @@ -554,16 +546,23 @@ func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, runt Ports: ports, VolumeMounts: volumeMounts, - WorkingDir: helper.GenerateWorkingDir(devbox, runtime), - Command: helper.GenerateCommand(devbox, runtime), - Args: helper.GenerateDevboxArgs(devbox, runtime), - Resources: helper.GenerateResourceRequirements(devbox, r.RequestCPURate, r.RequestMemoryRate, r.RequestEphemeralStorage, r.LimitEphemeralStorage), - }, + WorkingDir: helper.GetWorkingDir(devbox), + Command: helper.GetCommand(devbox), + Args: helper.GetArgs(devbox), + Resources: helper.GenerateResourceRequirements(devbox, r.RequestRate, r.EphemeralStorage)}, } terminationGracePeriodSeconds := 300 automountServiceAccountToken := false + runtimeClassName := devbox.Spec.RuntimeClassName + var runtimeClassNamePtr *string + if runtimeClassName == "" { + runtimeClassNamePtr = nil + } else { + runtimeClassNamePtr = ptr.To(runtimeClassName) + } + expectPod := &corev1.Pod{ ObjectMeta: objectMeta, Spec: corev1.PodSpec{ @@ -575,8 +574,11 @@ func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, runt Containers: containers, Volumes: volumes, - Tolerations: devbox.Spec.Tolerations, - Affinity: devbox.Spec.Affinity, + RuntimeClassName: runtimeClassNamePtr, + + NodeSelector: devbox.Spec.NodeSelector, + Tolerations: devbox.Spec.Tolerations, + Affinity: devbox.Spec.Affinity, }, } // set controller reference and finalizer diff --git a/controllers/devbox/internal/controller/helper/devbox.go b/controllers/devbox/internal/controller/helper/devbox.go index bd00faee4c8..1a7afcb02ba 100644 --- a/controllers/devbox/internal/controller/helper/devbox.go +++ b/controllers/devbox/internal/controller/helper/devbox.go @@ -16,7 +16,6 @@ package helper import ( "fmt" - "log/slog" "sort" "strings" @@ -31,6 +30,7 @@ import ( "k8s.io/utils/ptr" devboxv1alpha1 "github.com/labring/sealos/controllers/devbox/api/v1alpha1" + utilsresource "github.com/labring/sealos/controllers/devbox/internal/controller/utils/resource" "github.com/labring/sealos/controllers/devbox/label" ) @@ -38,16 +38,11 @@ const ( DevBoxPartOf = "devbox" ) -func GeneratePodLabels(devbox *devboxv1alpha1.Devbox, runtime *devboxv1alpha1.Runtime) map[string]string { +func GeneratePodLabels(devbox *devboxv1alpha1.Devbox) map[string]string { labels := make(map[string]string) - if runtime.Spec.Config.Labels != nil { - for k, v := range runtime.Spec.Config.Labels { - labels[k] = v - } - } - if devbox.Spec.ExtraLabels != nil { - for k, v := range devbox.Spec.ExtraLabels { + if devbox.Spec.Config.Labels != nil { + for k, v := range devbox.Spec.Config.Labels { labels[k] = v } } @@ -62,15 +57,10 @@ func GeneratePodLabels(devbox *devboxv1alpha1.Devbox, runtime *devboxv1alpha1.Ru return labels } -func GeneratePodAnnotations(devbox *devboxv1alpha1.Devbox, runtime *devboxv1alpha1.Runtime) map[string]string { +func GeneratePodAnnotations(devbox *devboxv1alpha1.Devbox) map[string]string { annotations := make(map[string]string) - if runtime.Spec.Config.Annotations != nil { - for k, v := range runtime.Spec.Config.Annotations { - annotations[k] = v - } - } - if devbox.Spec.ExtraAnnotations != nil { - for k, v := range devbox.Spec.ExtraAnnotations { + if devbox.Spec.Config.Annotations != nil { + for k, v := range devbox.Spec.Config.Annotations { annotations[k] = v } } @@ -200,10 +190,6 @@ func podContainerID(pod *corev1.Pod) string { } return "" } - -// PredicateCommitStatus returns the commit status of the pod -// if the pod container id is empty, it means the pod is pending or has't started, we can assume the image has not been committed -// otherwise, it means the pod has been started, we can assume the image has been committed func PredicateCommitStatus(pod *corev1.Pod) devboxv1alpha1.CommitStatus { if podContainerID(pod) == "" { return devboxv1alpha1.CommitStatusPending @@ -211,85 +197,6 @@ func PredicateCommitStatus(pod *corev1.Pod) devboxv1alpha1.CommitStatus { return devboxv1alpha1.CommitStatusSuccess } -func PodMatchExpectations(expectPod *corev1.Pod, pod *corev1.Pod) bool { - if len(pod.Spec.Containers) == 0 { - slog.Info("Pod has no containers") - return false - } - container := pod.Spec.Containers[0] - expectContainer := expectPod.Spec.Containers[0] - - // Check CPU and memory limits - if container.Resources.Requests.Cpu().Cmp(*expectContainer.Resources.Requests.Cpu()) != 0 { - slog.Info("CPU requests are not equal") - return false - } - if container.Resources.Limits.Cpu().Cmp(*expectContainer.Resources.Limits.Cpu()) != 0 { - slog.Info("CPU limits are not equal") - return false - } - if container.Resources.Requests.Memory().Cmp(*expectContainer.Resources.Requests.Memory()) != 0 { - slog.Info("Memory requests are not equal") - return false - } - if container.Resources.Limits.Memory().Cmp(*expectContainer.Resources.Limits.Memory()) != 0 { - slog.Info("Memory limits are not equal") - return false - } - - // Check Ephemeral Storage changes - if container.Resources.Requests.StorageEphemeral().Cmp(*expectContainer.Resources.Requests.StorageEphemeral()) != 0 { - slog.Info("Ephemeral-Storage requests are not equal") - return false - } - if container.Resources.Limits.StorageEphemeral().Cmp(*expectContainer.Resources.Limits.StorageEphemeral()) != 0 { - slog.Info("Ephemeral-Storage limits are not equal") - return false - } - - // Check environment variables - if len(container.Env) != len(expectContainer.Env) { - return false - } - for _, env := range container.Env { - found := false - for _, expectEnv := range expectContainer.Env { - if env.Name == "SEALOS_COMMIT_IMAGE_NAME" { - found = true - break - } - if env.Name == expectEnv.Name && env.Value == expectEnv.Value { - found = true - break - } - } - if !found { - slog.Info("Environment variables are not equal", "env not found", env.Name, "env value", env.Value) - return false - } - } - - // Check ports - if len(container.Ports) != len(expectContainer.Ports) { - return false - } - for _, expectPort := range expectContainer.Ports { - found := false - for _, podPort := range container.Ports { - if expectPort.ContainerPort == podPort.ContainerPort && expectPort.Protocol == podPort.Protocol { - found = true - break - } - } - if !found { - slog.Info("Ports are not equal") - return false - } - } - - return true -} - func GenerateDevboxEnvVars(devbox *devboxv1alpha1.Devbox, nextCommitHistory *devboxv1alpha1.CommitHistory) []corev1.EnvVar { // if devbox.Spec.Squash is true, and devbox.Status.CommitHistory has success commit history, we need to set SEALOS_COMMIT_IMAGE_SQUASH to true doSquash := false @@ -347,13 +254,13 @@ func GetLastSuccessCommitHistory(devbox *devboxv1alpha1.Devbox) *devboxv1alpha1. return nil } -func GetLastSuccessCommitImageName(devbox *devboxv1alpha1.Devbox, runtime *devboxv1alpha1.Runtime) string { +func GetLastSuccessCommitImageName(devbox *devboxv1alpha1.Devbox) string { if len(devbox.Status.CommitHistory) == 0 { - return runtime.Spec.Config.Image + return devbox.Spec.Image } commit := GetLastSuccessCommitHistory(devbox) if commit == nil { - return runtime.Spec.Config.Image + return devbox.Spec.Image } return commit.Image } @@ -398,76 +305,63 @@ func GenerateSSHVolume(devbox *devboxv1alpha1.Devbox) corev1.Volume { } } -func GenerateResourceRequirements(devbox *devboxv1alpha1.Devbox, - requestCPURate, requestMemoryRate float64, - requestEphemeralStorage, limitEphemeralStorage string, -) corev1.ResourceRequirements { +// GenerateResourceRequirements generates the resource requirements for the Devbox pod +func GenerateResourceRequirements(devbox *devboxv1alpha1.Devbox, requestRate utilsresource.RequestRate, ephemeralStorage utilsresource.EphemeralStorage) corev1.ResourceRequirements { return corev1.ResourceRequirements{ - Requests: calculateResourceRequest( - corev1.ResourceList{ - corev1.ResourceCPU: devbox.Spec.Resource["cpu"], - corev1.ResourceMemory: devbox.Spec.Resource["memory"], - corev1.ResourceEphemeralStorage: resource.MustParse(requestEphemeralStorage), - }, - requestCPURate, requestMemoryRate, - ), - Limits: corev1.ResourceList{ - corev1.ResourceCPU: devbox.Spec.Resource["cpu"], - corev1.ResourceMemory: devbox.Spec.Resource["memory"], - corev1.ResourceEphemeralStorage: resource.MustParse(limitEphemeralStorage), - }, + Limits: calculateResourceLimit(devbox.Spec.Resource, ephemeralStorage), + Requests: calculateResourceRequest(devbox.Spec.Resource, requestRate, ephemeralStorage), } } -func IsExceededQuotaError(err error) bool { - return strings.Contains(err.Error(), "exceeded quota") +func calculateResourceLimit(original corev1.ResourceList, ephemeralStorage utilsresource.EphemeralStorage) corev1.ResourceList { + limit := original.DeepCopy() + // If ephemeral storage limit is not set, set it to default limit + if l, ok := limit[corev1.ResourceEphemeralStorage]; !ok { + limit[corev1.ResourceEphemeralStorage] = ephemeralStorage.DefaultLimit + } else { + // Check if the resource limit for ephemeral storage is set and compare it, if it is exceeded the maximum limit, set it to maximum limit + if l.AsApproximateFloat64() > ephemeralStorage.MaximumLimit.AsApproximateFloat64() { + limit[corev1.ResourceEphemeralStorage] = ephemeralStorage.MaximumLimit + } + } + return limit } -func calculateResourceRequest(limit corev1.ResourceList, requestCPURate, requestMemoryRate float64) corev1.ResourceList { - if limit == nil { - return nil - } - request := make(corev1.ResourceList) +func calculateResourceRequest(original corev1.ResourceList, requestRate utilsresource.RequestRate, ephemeralStorage utilsresource.EphemeralStorage) corev1.ResourceList { + // deep copy limit to request, only cpu and memory are calculated + request := original.DeepCopy() // Calculate CPU request - if cpu, ok := limit[corev1.ResourceCPU]; ok { + if cpu, ok := original[corev1.ResourceCPU]; ok { cpuValue := cpu.AsApproximateFloat64() - cpuRequest := cpuValue / requestCPURate + cpuRequest := cpuValue / requestRate.CPU request[corev1.ResourceCPU] = *resource.NewMilliQuantity(int64(cpuRequest*1000), resource.DecimalSI) } // Calculate memory request - if memory, ok := limit[corev1.ResourceMemory]; ok { + if memory, ok := original[corev1.ResourceMemory]; ok { memoryValue := memory.AsApproximateFloat64() - memoryRequest := memoryValue / requestMemoryRate + memoryRequest := memoryValue / requestRate.Memory request[corev1.ResourceMemory] = *resource.NewQuantity(int64(memoryRequest), resource.BinarySI) } - - if ephemeralStorage, ok := limit[corev1.ResourceEphemeralStorage]; ok { - request[corev1.ResourceEphemeralStorage] = ephemeralStorage - } - + // Set ephemeral storage request to default request + request[corev1.ResourceEphemeralStorage] = ephemeralStorage.DefaultRequest return request } -// GenerateWorkingDir generates the working directory for the Devbox pod -func GenerateWorkingDir(devbox *devboxv1alpha1.Devbox, runtime *devboxv1alpha1.Runtime) string { - if devbox.Spec.WorkingDir != "" { - return devbox.Spec.WorkingDir - } - return runtime.Spec.Config.WorkingDir +// GetWorkingDir get the working directory for the Devbox pod +func GetWorkingDir(devbox *devboxv1alpha1.Devbox) string { + return devbox.Spec.Config.WorkingDir } -// GenerateCommand generates the command for the Devbox pod -func GenerateCommand(devbox *devboxv1alpha1.Devbox, runtime *devboxv1alpha1.Runtime) []string { - if len(devbox.Spec.Command) != 0 { - return devbox.Spec.Command - } - return runtime.Spec.Config.Command +// GetCommand get the command for the Devbox pod +func GetCommand(devbox *devboxv1alpha1.Devbox) []string { + return devbox.Spec.Config.Command } -// GenerateDevboxArgs generates the arguments for the Devbox pod -func GenerateDevboxArgs(devbox *devboxv1alpha1.Devbox, runtime *devboxv1alpha1.Runtime) []string { - if len(devbox.Spec.Args) != 0 { - return devbox.Spec.Args - } - return runtime.Spec.Config.Args +// GetArgs get the arguments for the Devbox pod +func GetArgs(devbox *devboxv1alpha1.Devbox) []string { + return devbox.Spec.Config.Args +} + +func IsExceededQuotaError(err error) bool { + return strings.Contains(err.Error(), "exceeded quota") } diff --git a/controllers/devbox/internal/controller/utils/matcher/matcher.go b/controllers/devbox/internal/controller/utils/matcher/matcher.go new file mode 100644 index 00000000000..dfad8e59f9f --- /dev/null +++ b/controllers/devbox/internal/controller/utils/matcher/matcher.go @@ -0,0 +1,154 @@ +// Copyright © 2024 sealos. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package matcher + +import ( + "log/slog" + + corev1 "k8s.io/api/core/v1" +) + +type PodMatcher interface { + Match(expectPod *corev1.Pod, pod *corev1.Pod) bool +} + +type ResourceMatcher struct{} + +func (r ResourceMatcher) Match(expectPod *corev1.Pod, pod *corev1.Pod) bool { + if len(pod.Spec.Containers) == 0 { + slog.Info("Pod has no containers") + return false + } + container := pod.Spec.Containers[0] + expectContainer := expectPod.Spec.Containers[0] + + if container.Resources.Requests.Cpu().Cmp(*expectContainer.Resources.Requests.Cpu()) != 0 { + slog.Info("CPU requests are not equal") + return false + } + if container.Resources.Limits.Cpu().Cmp(*expectContainer.Resources.Limits.Cpu()) != 0 { + slog.Info("CPU limits are not equal") + return false + } + if container.Resources.Requests.Memory().Cmp(*expectContainer.Resources.Requests.Memory()) != 0 { + slog.Info("Memory requests are not equal") + return false + } + if container.Resources.Limits.Memory().Cmp(*expectContainer.Resources.Limits.Memory()) != 0 { + slog.Info("Memory limits are not equal") + return false + } + return true +} + +type EphemeralStorageMatcher struct{} + +func (e EphemeralStorageMatcher) Match(expectPod *corev1.Pod, pod *corev1.Pod) bool { + if len(pod.Spec.Containers) == 0 { + slog.Info("Pod has no containers") + return false + } + container := pod.Spec.Containers[0] + expectContainer := expectPod.Spec.Containers[0] + + if container.Resources.Limits.StorageEphemeral().Cmp(*expectContainer.Resources.Limits.StorageEphemeral()) != 0 { + slog.Info("Ephemeral-Storage limits are not equal") + return false + } + if container.Resources.Requests.StorageEphemeral().Cmp(*expectContainer.Resources.Requests.StorageEphemeral()) != 0 { + slog.Info("Ephemeral-Storage requests are not equal") + return false + } + return true +} + +type EnvVarMatcher struct{} + +func (e EnvVarMatcher) Match(expectPod *corev1.Pod, pod *corev1.Pod) bool { + if len(pod.Spec.Containers) == 0 { + slog.Info("Pod has no containers") + return false + } + container := pod.Spec.Containers[0] + expectContainer := expectPod.Spec.Containers[0] + + if len(container.Env) != len(expectContainer.Env) { + slog.Info("Environment variable count mismatch") + return false + } + + for _, env := range container.Env { + found := false + for _, expectEnv := range expectContainer.Env { + if env.Name == "SEALOS_COMMIT_IMAGE_NAME" { + found = true + break + } + if env.Name == expectEnv.Name && env.Value == expectEnv.Value { + found = true + break + } + } + if !found { + slog.Info("Environment variables are not equal", "env not found", env.Name, "env value", env.Value) + return false + } + } + return true +} + +type PortMatcher struct{} + +func (p PortMatcher) Match(expectPod *corev1.Pod, pod *corev1.Pod) bool { + if len(pod.Spec.Containers) == 0 { + slog.Info("Pod has no containers") + return false + } + container := pod.Spec.Containers[0] + expectContainer := expectPod.Spec.Containers[0] + + if len(container.Ports) != len(expectContainer.Ports) { + slog.Info("Port count mismatch") + return false + } + + for _, expectPort := range expectContainer.Ports { + found := false + for _, podPort := range container.Ports { + if expectPort.ContainerPort == podPort.ContainerPort && expectPort.Protocol == podPort.Protocol { + found = true + break + } + } + if !found { + slog.Info("Ports are not equal") + return false + } + } + return true +} + +// PredicateCommitStatus returns the commit status of the pod +// if the pod container id is empty, it means the pod is pending or has't started, we can assume the image has not been committed +// otherwise, it means the pod has been started, we can assume the image has been committed + +func PodMatchExpectations(expectPod *corev1.Pod, pod *corev1.Pod, matchers ...PodMatcher) bool { + for _, matcher := range matchers { + if !matcher.Match(expectPod, pod) { + return false + } + } + return true +} diff --git a/controllers/devbox/internal/controller/helper/devbox_test.go b/controllers/devbox/internal/controller/utils/matcher/matcher_test.go similarity index 95% rename from controllers/devbox/internal/controller/helper/devbox_test.go rename to controllers/devbox/internal/controller/utils/matcher/matcher_test.go index f3563b743fb..1317615196a 100644 --- a/controllers/devbox/internal/controller/helper/devbox_test.go +++ b/controllers/devbox/internal/controller/utils/matcher/matcher_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package helper +package matcher import ( "testing" @@ -156,9 +156,16 @@ func TestPodMatchExpectations(t *testing.T) { }, } + matchers := []PodMatcher{ + ResourceMatcher{}, + EnvVarMatcher{}, + PortMatcher{}, + EphemeralStorageMatcher{}, + } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := PodMatchExpectations(expectPod, tt.pod) + result := PodMatchExpectations(expectPod, tt.pod, matchers...) if result != tt.expected { t.Errorf("CheckPodConsistency() = %v, expected %v", result, tt.expected) } diff --git a/controllers/devbox/internal/controller/utils/resource/resource.go b/controllers/devbox/internal/controller/utils/resource/resource.go new file mode 100644 index 00000000000..3e0e4781360 --- /dev/null +++ b/controllers/devbox/internal/controller/utils/resource/resource.go @@ -0,0 +1,30 @@ +// Copyright © 2024 sealos. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package resource + +import ( + "k8s.io/apimachinery/pkg/api/resource" +) + +type RequestRate struct { + CPU float64 + Memory float64 +} + +type EphemeralStorage struct { + DefaultRequest resource.Quantity + DefaultLimit resource.Quantity + MaximumLimit resource.Quantity +} diff --git a/docs/4.0/docs/Community.md b/docs/4.0/docs/Community.md index 6722b4c49c5..2f1b01d6710 100644 --- a/docs/4.0/docs/Community.md +++ b/docs/4.0/docs/Community.md @@ -3,5 +3,5 @@ Sealos is an open source project that is driven by the participation of users and contributors. If you have questions or suggestions about using the product while reading this documentation, please try the following ways to seek support. Our team and community will do their best to help you. + 💬 Join our [Discord server](https://discord.gg/qzBmGGZGk7) is to chat with Sealos developers and other Sealos users. This is a good place to learn about Sealos and Kubernetes, ask questions, and share your experiences. -+ 🐦 Tweet at @sealosio on [Twitter](https://twitter.com/sealosio) and follow us. ++ 🐦 Tweet at @Sailos_io on [Twitter](https://twitter.com/Sailos_io) and follow us. + 🐞 Create [GitHub Issues](https://github.com/labring/sealos/issues/new/choose) for bug reports and feature requests. \ No newline at end of file diff --git a/docs/4.0/i18n/zh-Hans/Community.md b/docs/4.0/i18n/zh-Hans/Community.md index 1c7654defbd..c015bb6aee2 100644 --- a/docs/4.0/i18n/zh-Hans/Community.md +++ b/docs/4.0/i18n/zh-Hans/Community.md @@ -7,5 +7,5 @@ Sealos 是一个由用户和贡献者参与推动的开源项目,如果您对 ![](https://oss.laf.run/htr4n1-images/sealos-qr-code.jpg) + 💬 加入我们的 [Discord 服务器](https://discord.gg/qzBmGGZGk7),与 Sealos 开发者和终端用户进行交流。 -+ 🐦 在 [Twitter](https://twitter.com/sealosio) 上关注我们。 ++ 🐦 在 [Twitter](https://twitter.com/Sailos_io) 上关注我们。 + 🐞 请将任何 Sealos 的 Bug、问题和需求提交到 [GitHub Issue](https://github.com/labring/sealos/issues/new/choose)。 \ No newline at end of file diff --git a/docs/5.0/docs/community.md b/docs/5.0/docs/community.md index 696dbfcc7fa..eb8c107e584 100644 --- a/docs/5.0/docs/community.md +++ b/docs/5.0/docs/community.md @@ -1,6 +1,6 @@ --- keywords: [Sealos, open source project, Kubernetes, Discord server, GitHub Issues] -description: Sealos is an open source project driven by user participation. Join our Discord server, tweet at @sealosio, or create GitHub Issues for support. +description: Sealos is an open source project driven by user participation. Join our Discord server, tweet at @Sailos_io, or create GitHub Issues for support. --- # Community @@ -11,5 +11,5 @@ team and community will do their best to help you. + 💬 Join our [Discord server](https://discord.gg/qzBmGGZGk7) is to chat with Sealos developers and other Sealos users. This is a good place to learn about Sealos and Kubernetes, ask questions, and share your experiences. -+ 🐦 Tweet at @sealosio on [Twitter](https://twitter.com/sealosio) and follow us. ++ 🐦 Tweet at @Sailos_io on [Twitter](https://twitter.com/Sailos_io) and follow us. + 🐞 Create [GitHub Issues](https://github.com/labring/sealos/issues/new/choose) for bug reports and feature requests. \ No newline at end of file diff --git a/docs/5.0/i18n/zh-Hans/community.md b/docs/5.0/i18n/zh-Hans/community.md index a7f0dd161af..274cb076d24 100644 --- a/docs/5.0/i18n/zh-Hans/community.md +++ b/docs/5.0/i18n/zh-Hans/community.md @@ -12,5 +12,5 @@ Sealos 是一个由用户和贡献者参与推动的开源项目,如果您对 ![](https://oss.laf.run/htr4n1-images/sealos-qr-code.jpg) + 💬 加入我们的 [Discord 服务器](https://discord.gg/qzBmGGZGk7),与 Sealos 开发者和终端用户进行交流。 -+ 🐦 在 [Twitter](https://twitter.com/sealosio) 上关注我们。 ++ 🐦 在 [Twitter](https://twitter.com/Sailos_io) 上关注我们。 + 🐞 请将任何 Sealos 的 Bug、问题和需求提交到 [GitHub Issue](https://github.com/labring/sealos/issues/new/choose)。 \ No newline at end of file diff --git a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/envoy-resource.png b/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/envoy-resource.png deleted file mode 100644 index c3db14f508d..00000000000 Binary files a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/envoy-resource.png and /dev/null differ diff --git a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-controller.png b/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-controller.png deleted file mode 100644 index c4835a35efb..00000000000 Binary files a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-controller.png and /dev/null differ diff --git a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-gateway.png b/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-gateway.png deleted file mode 100644 index e607ff6143b..00000000000 Binary files a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-gateway.png and /dev/null differ diff --git a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/nginx-resource.png b/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/nginx-resource.png deleted file mode 100644 index aa36990d2d9..00000000000 Binary files a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/nginx-resource.png and /dev/null differ diff --git a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/index.md b/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/index.md deleted file mode 100644 index 11107935c33..00000000000 --- a/docs/blog/en/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/index.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -slug: how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice -title: How Sealos Cloud Mastered Multi-Tenancy with the Right Gateway Choice -description: Explore how Sealos Cloud optimizes its gateway selection amidst handling hundreds of thousands of Ingress rules and strict multi-tenancy requirements. From the limitations of Nginx Ingress to the eventual choice of Higress, this article delves deep into the performance, stability, and security of various gateway options, offering valuable insights for gateway selection in multi-tenant scenarios in public network environments. -authors: [fanux] -tags: [Kubernetes, Sealos, Gateway] -keywords: [Cloud Operating System, Sealos, K8s, Cloud Native, Gateway, Envoy, Ingress] -image: images/feature.jpg -date: 2024-01-20T10:00 ---- - -[Sealos](https://sealos.io) Cloud has stretched the capabilities of nearly all the leading open-source gateways to their limits. This article aims to serve as a practical guide, helping to navigate common challenges and offering advice for choosing the right gateway. - - - -## Complex Challenges in Sealos Cloud - -Ever since the launch of [Sealos Cloud](https://cloud.sealos.io), the platform has seen a meteoric rise in user numbers, currently standing at **87,000 registered users**. Each of these users creates applications, and each application demands its individual access point, leading to an extraordinarily large number of routing entries across the entire cluster. **This necessitates support for ingress capabilities on the scale of hundreds of thousands.** - -Furthermore, offering shared cluster services on the public internet places stringent demands on multi-tenancy. It's crucial that user routes are completely isolated from one another to ensure optimal route integrity, demanding high-quality isolation and sophisticated traffic control measures. - -The exposure to potential cyber threats is extensive in public clouds. Hackers not only target applications running on the cloud but also aim at the platform's outbound network infrastructure, thereby intensifying the security challenges. - -The demands on controller performance and stability are substantial. Many controllers, when faced with an increasing number of routing entries, consume extensive resources, which can sometimes lead to Out of Memory (OOM) issues, ultimately causing gateway failures. - -## Nginx Ingress - -Our initial choice was Nginx Ingress, but we eventually faced several critical issues that proved to be deal-breakers: - -* **Reload dilemma**: Every ingress modification results in brief disconnections. In a cluster bustling with users, the high frequency of ingress adjustments leads to consistent network instability. -* **Unreliable long connections**: Due to the dynamic nature of adjustments, long-term connections are prone to frequent disruptions. -* **Suboptimal performance:** The gateway's responsiveness is sluggish, and it's relatively resource-hungry. - -These significant concerns have led us to move away from gateways rooted in Nginx architecture. Our empirical testing revealed that Envoy-based gateways significantly outperform others, showing minimal performance overhead in both the control and data planes. - -Here's a glimpse of Envoy's performance: - -![](images/envoy-resource.png) - -In contrast, here's what we observed with Nginx's performance: - -![](images/nginx-resource.png) - -The disparity is pronounced, leading us to decisively set aside options based on Nginx and fully embrace the robust capabilities of Envoy. - -## APISIX - -[APISIX](https://github.com/apache/apisix) is a commendable project, particularly in addressing Nginx reload issues. At [Laf](https://github.com/labring/laf), we initially embraced APISIX. However, we encountered instability with its Ingress Controller, leading to frequent major disruptions and controller OOM issues. Despite our preference for APISIX, these persistent issues necessitated a switch to an alternative gateway. The APISIX community is actively working on these challenges, and we look forward to its continued improvement. - -In summary, while APISIX demonstrates excellent stability, its controller still requires significant optimization and stability enhancements. The community provides robust support, but **due to our immediate operational challenges, we had to transition to a different gateway solution.** - -## Cilium Gateway - -Having switched our [CNI](https://sealos.io/docs/self-hosting/lifecycle-management/quick-start/deploy-kubernetes#install-kubernetes-cluster) to Cilium early on, we recognized its potential and contemplated using the Cilium Gateway. However, reality presented its challenges. - -[Cilium Gateway](https://cilium.io/use-cases/gateway-api/) exclusively supports LB mode, creating a dependency on cloud provider LBs. Given our need for private deployment scenarios, this dependence was undesirable. In terms of stability, the Ingress activation delay in scenarios with numerous routes was significantly prolonged, taking minutes instead of the preferable 5 seconds. **Therefore, we concluded that it's necessary to wait for further development in this aspect.** - -## Envoy Gateway - -In the realm of Kubernetes (K8s) standards, there is a noticeable shift from the traditional Ingress to the Gateway standard. Our foundational preference for Envoy leads us to consider the implementation of the [Envoy Gateway](https://github.com/envoyproxy/gateway) as a promising option. Our exploration of the Envoy Gateway revealed that it is still in a preliminary phase, plagued by several instability issues like memory overflows, path policies not being effective, and certain functionalities not working in the merge gateway mode. We are actively engaged in resolving these issues and are contributing to the upstream community with constructive feedback and improvements. Our aim is to nurture the Envoy Gateway to a level where it becomes fully viable for production environments. - -## The High-Prestige but Less Practical Gateway Standard - -The Gateway standard finds itself in a tricky situation. It appears the designers may not have thoroughly explored multi-tenant environments in practice. When a cluster is shared among multiple tenants, it is crucial to clearly define and separate the rights and responsibilities of administrators and users. Gateway's initial design overlooked this aspect. For instance: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: eg -spec: - gatewayClassName: eg - listeners: - - name: http - port: 80 - protocol: HTTP - # hostname: "*.example.com" - - name: https - port: 443 - protocol: HTTPS - # hostname: "*.example.com" - tls: - mode: Terminate - certificateRefs: - - kind: Secret - name: example-com -``` - -Settings like listening ports should ideally be managed by cluster administrators rather than ordinary users. On the other hand, configuring TLS certificates should be more user-centric, although administrators might still need some control. However, in this setup, the delineation of permissions is unclear. Consequently, users are also given the ability to configure the Gateway, leading to the necessity for intricate permission control in the controller, such as managing port whitelists and detecting conflicts. - -A more sophisticated design approach might involve relocating tenant-level fields to HTTPRoute or establishing a separate CRD, thereby clarifying the distinction between regular users and super administrators. The current approach is functional, but it tends to be somewhat muddled and less efficient. - -## Higress: The Clear Winner - -Beyond the key projects we focused on, numerous others were evaluated but are not listed here. Ultimately, Sealos chose [Higress](https://github.com/alibaba/higress) for its gateway needs. - -Our criteria for gateway selection were straightforward: we sought a solution that was not only functionally adequate but also highly stable. Higress emerged as our choice, essentially by process of elimination. - -**Stability was our primary concern. Among the contenders, Higress was the only one meeting our production standards**, although some challenges arose. Thankfully, the proactive Higress community swiftly resolved these issues. Notable challenges included: - -1. **Ingress Activation Speed** – Initially, it took over two minutes for new routes to activate when dealing with many entries. This was optimized by the community to about 3 seconds, a remarkable improvement that eliminated the need for further optimization, as it now outpaces the container Ready time. Higress's use of an incremental configuration loading approach ensures exceptional performance, even with a high volume of routing entries. -2. **Controller OOM** – Previously, the controller faced memory issues due to high resource consumption without dynamic loading. These issues have been effectively addressed. -3. **Timeout Issues** – In one of our primary clusters, we encountered sporadic request timeouts related to the onDemandRDS configuration. We've temporarily disabled this feature and are investigating further. This problem was not present in our other clusters. - -From a security standpoint, many issues we faced were linked to performance bottlenecks, such as traffic surges overwhelming the gateway. This underscores the vital importance of gateway performance. In our tests, Envoy demonstrated remarkable robustness, and the design of the controller proved to be a critical factor in operational success. Higress has shown exceptional performance in this regard: - -![](images/higress-controller.png) - -![](images/higress-gateway.png) - -Given our extensive routing and high-volume traffic, Higress stands out for its remarkably low resource demand. - -Higress also boasts compatibility with Nginx Ingress syntax, mainly in annotations. Our prior reliance on Ingress meant there was almost no need for code migration, allowing for a swift upgrade process within minutes. - -To further encourage community development, we also have recommendations for Higress: - -* Enhanced support for the Gateway standard is needed. Although it already supports the v1 version, it lacks full compatibility with the features available on Ingress. -* We suggest the introduction of more sophisticated functionalities, especially in areas like security and circuit breaking. We are open to paying for these features, but as our platform evolves, stronger functionalities become necessary. -* We advise developing additional peripheral features through a plugin mechanism, aiming to make the core features more cohesive, simpler, and more reliable. - -## Summary - -Gateways are a fundamental component for cloud services and applications. As our scale grows, we face an array of new challenges. Our aim is to build strong collaborations with the broader community, enhancing the development of open-source gateways and benefiting a larger pool of developers. - -The gateways mentioned are all of high quality. Sealos not utilizing them is not a commentary on their effectiveness but a reflection of our unique and stringent scenarios. Gateways that support multi-tenancy in public internet environments are few. Therefore, it's crucial for decision-makers to consider their specific scenarios. Our choices should serve as a guideline, and Sealos is dedicated to maintaining an open approach in monitoring the evolution of various gateways. - -We are immensely grateful to the Higress open-source community for their substantial support and to the Alibaba Cloud Native Team for contributing such a valuable project to the community. diff --git a/docs/blog/en/2023/k8s-multi-tenancy.md b/docs/blog/en/2023/k8s-multi-tenancy.md deleted file mode 100644 index 99239400d71..00000000000 --- a/docs/blog/en/2023/k8s-multi-tenancy.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -slug: k8s-multi-tenancy -title: The Promise and Challenges of Kubernetes Multi-Tenant -description: explores the value proposition of multi-tenant Kubernetes, implementation hurdles, and potential solutions to unlock its benefits. -authors: [fanux] -tags: [Kubernetes, Sealos, Multi-Tenant] -keywords: [Cloud Operating System, Sealos, K8s, Cloud Native, Cloud Computing, Cloud OS, PaaS, Multi-Tenant, Runtime Isolation, Namespace] -image: https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-17-36-fBsk9p.jpg -date: 2023-11-29T10:00 ---- - -In today's business landscape, managing cloud and server resources is becoming increasingly unwieldy as companies diversify and scale. While powerful, Kubernetes lacks native support for multi-tenancy - the ability to securely isolate multiple tenant workloads. This gap creates deployment friction for teams and missed efficiency opportunities for enterprises. - -**This article explores the value proposition of multi-tenant Kubernetes, implementation hurdles, and potential solutions to unlock its benefits.** - - - -## The Potential of Multi-Tenant Kubernetes - -Multi-tenancy refers to an architecture allowing multiple users or "tenants" to share resources from the same system while keeping data isolated and secure. For Kubernetes, this means running workloads from different teams on a shared cluster without risk of resource conflicts, data leaks, or security issues. - -![Diagram of single vs multi-tenant Kubernetes](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-rLPyaY.jpg) - -### Pain Points of Single-Tenant Setups - -Consider an enterprise Kubernetes cluster used by 20 internal departments. Without multi-tenancy, several pain points emerge: - -1. **Inefficiency** - Deployments get bottlenecked through cluster administrators, hampering velocity. -2. **Underutilization** - Workloads cannot mix, leading to resource stranding. -3. **Sprawl** - Lacking isolation allows cluster entanglement over time. -4. **Limitations** - Fixed single-tenant structure strains under changing demands. - -![Comparison table of single vs multi-tenant Kubernetes](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-15-53-DGg4ig.png) - -### The Multi-Tenant Advantage - -Conversely, effective multi-tenancy unlocks greater cloud agility: - -1. **Organization** - Teams self-manage resources without wasteful allocation conflicts. -2. **Velocity** - Services rapidly provision without administrative bottlenecks. -3. **Efficiency** - Cluster administrators focus holistically rather than application-by-application. -4. **Resilience** - Workload isolation enhances stability despite diverse deployments. - -## Navigating Multi-Tenancy Challenges in Kubernetes - -In the Kubernetes (K8s) landscape, establishing a multi-tenant architecture is a complex endeavor, transcending the mere application of namespaces and involving an array of technical intricacies. - -### Challenge 1: Curbing Over-Privilege Risks - -Central to a multi-tenant K8s framework is the strict regulation of user permissions. In scenarios where a cluster is shared among several users, overly privileged individuals can pose a serious threat. Prohibitions against accessing server nodes or executing node-level commands, such as `kubectl get node`, are essential. Further, it's crucial to curtail other high-risk activities, including the activation of container privileged modes, and sharing of host filesystems, ports, and networks. - -Sealos addresses these concerns through a multi-faceted isolation approach. It employs OpenEBS for block-level storage isolation, Firecracker and Cloud Hypervisor for computational isolation, and Cilium for network isolation, ensuring that the activities of one tenant do not adversely affect others. - -### Challenge 2: User Identity, Authorization, and Namespace Ties - -Inherent to K8s is the absence of a native user management framework. This necessitates the creation of a user identity system, integration with external user management platforms, and issuance of unique kubeconfig files or tokens. Moreover, it's imperative to forge a multifaceted linkage between users and namespaces, coupled with the distribution of tailored permissions. - -![Image Depicting User-Namespace Association](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-Dfn5xa.png) - -Sealos's framework enables administrators to effectively slot users into designated namespaces and regulate their roles, thereby achieving a granular control over permissions. This guarantees that users access only the resources they are legitimately permitted to use. - -![Image Illustrating User Permissions Management](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-wknQxI.png) - -![Image Showcasing User Role Control](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-RQFrTB.png) - -### Challenge 3: Metering and Managing Quotas - -A critical aspect of multi-tenancy in K8s is the equitable distribution and meticulous tracking of resource usage, including CPU, memory, disk, and network utilization. Managing excess usage and differentiating between internal and external network traffic are particularly challenging, as is accurately attributing traffic to specific containers and tenants. - -Utilizing eBPF technology, Sealos adeptly monitors network traffic, correlating it with tenant information and storing it in a database for precise billing and resource management. For compute and storage resources, Sealos relies on controllers to gather and administer relevant data, ensuring efficient resource oversight. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-36-HsycaI.png) - -## Extreme Multi-Tenancy - The Sealos Challenge - -In the realm of multi-tenancy, Sealos embarks on an ambitious journey, operating within the unpredictable confines of a public network. This scenario invites any developer to join and partake in a communal Kubernetes cluster, which inherently raises substantial security and stability risks. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-54-kbCMsN.png) - -The method adopted by Sealos brings forth distinct benefits: cost-effectiveness, as it negates the need for users to independently build and manage their clusters, leading to significant cost reductions in cloud services. It also enhances resource utilization, allowing container operations on a smaller scale, thereby leveraging the platform’s flexibility and resources. Crucially, establishing strong isolation in such a public network setting can bolster security and stability. - -Nevertheless, Sealos is confronted with a series of formidable challenges: - -**Challenge 1: Overcoming Gateway Limitations** The substantial user base of Sealos generates immense traffic, pushing the boundaries of many mainstream open-source gateways. A single user update can potentially impact the entire user base, as systems like Nginx require configuration reloads. Moreover, specific well-known gateways, which remain unnamed, struggle with issues like CPU overload and delays in configuration effectiveness. Sealos has proactively engaged with upstream communities for potential improvements. - -**Challenge 2: Addressing Runtime Isolation** Strong isolation is critical for ensuring security in Sealos's multi-tenant environment, but current mainstream runtime environments fall short of meeting these requirements. For example, Firecracker's inadequate GPU support presents a significant limitation for high-performance computing applications. - -**Challenge 3: Ensuring Storage Isolation** A paramount concern for Sealos is the isolation of tenant data to prevent unauthorized access or data breaches. The goal is to implement block-level storage isolation, a challenging but necessary endeavor. - -**Challenge 4: Network Metering and Contention Management** Managing and accurately metering network resources is essential in a multi-tenant infrastructure. Sealos is committed to distributing these resources fairly, particularly in situations where resource contention occurs, to ensure that all users have fair and efficient access to network resources. - -**Summary** - -Only with the advancement and maturity of multi-tenancy can the true essence of a cloud be actualized, harnessing over ninety percent of its inherent capabilities. Facing the complexities and unpredictabilities of the public internet, Sealos excels by providing secure and isolated multi-tenant environments. It achieves this while ensuring efficiency and reducing operational costs. The underlying technology Sealos uses is elegantly designed, catering perfectly to businesses where a single cloud platform is shared among all developers. \ No newline at end of file diff --git a/docs/blog/en/2023/sealos-release.md b/docs/blog/en/2023/sealos-release.md deleted file mode 100644 index 5725dc826a9..00000000000 --- a/docs/blog/en/2023/sealos-release.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -slug: sealos-release -title: "Sealos: Revolutionizing Cloud Computing with a User-Friendly Operating System" -description: Explore the journey of Sealos, a grand cloud operating system project, from its inception as a simple Kubernetes installer to a comprehensive platform transforming cloud computing. Dive into the creator's story, the evolution of Sealos, and how it simplifies cloud operations for businesses and individuals alike. -authors: [fanux] -tags: [Kubernetes, Sealos] -keywords: [cloud operating system, Sealos, Kubernetes, cloud native, Cloud computing, cluster image, Sealer, cloud-native technologies] -image: https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting5@main/uPic/2023-08-31-09-52-gLmSek.jpg -date: 2023-06-13T10:00 ---- - -In the ever-evolving landscape of cloud computing, a groundbreaking innovation has emerged, reshaping our understanding of cloud infrastructures. Sealos, transcending its initial scope as a Kubernetes installation tool, has emerged as a pioneering [cloud operating system](https://sealos.io). In this blog, we'll delve into the journey of Sealos, from its inception to becoming a cornerstone in cloud computing, offering unparalleled user experience and efficiency. - - - -## The Inception of Sealos - -It all started one quiet night in 2018. A single line of code marked the beginning of something extraordinary. Initially named "kubeinit," the project soon outgrew its initial scope. Recognizing the need for a broader vision, the project was aptly renamed Sealos. It was not merely about installing Kubernetes; it was about piecing together a comprehensive [cloud operating system](https://sealos.io). This pivotal moment set the stage for the evolution of Sealos, a vision that would redefine cloud computing. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-08-21-15-15-rcAujq.webp) - -## The Entrepreneurial Journey and Initial Challenges - -The journey of Sealos is a tale of challenges, perseverance, and innovation. When Sealos made its debut on the Alibaba Cloud Marketplace at 15 yuan per copy, the expectation of commercial success was modest. But the first sale, a mere 15 yuan, was a monumental milestone. It was more than just a transaction; it was a validation of potential, a glimpse into a future of endless possibilities. - -However, this initial success came with its own set of challenges. Providing after-sales service proved to be a Herculean task, demanding constant attention and problem-solving, often at the cost of personal time. This phase was crucial, laying the groundwork for understanding user needs and driving Sealos towards excellence. - -## Enhancements and Innovations in Sealos - -The development journey of Sealos has been marked by constant innovation and improvement. The initial version, while functional, was just the beginning. Recognizing the need for a more robust solution, Sealos evolved into its second iteration, leveraging Ansible for enhanced performance. - -However, the pursuit of perfection never ceased. The third version of Sealos was a breakthrough, simplifying load balancing and eliminating dependencies, making it a pinnacle in installation simplicity. This relentless pursuit of innovation reflects the core ethos of Sealos – to simplify complex cloud operations while enhancing efficiency and reliability. - -## Focus on Installation: A Strategic Choice - -Installation is often the first encounter users have with any software, and for Sealos, it was crucial to make this experience as smooth as possible. Focusing on installation was a strategic choice, ensuring that users could easily step into the world of cloud-native technologies. This focus also set the stage for users to explore the full spectrum of Sealos' capabilities, fostering a deeper engagement with the platform. - -## Sealos at Alibaba: The Birth of Sealer - -During its tenure at Alibaba, Sealos underwent a significant transformation with the development of Sealer. This was a pivotal point, as Sealer brought unparalleled flexibility to the installation process. The concept of cluster imaging, akin to a "cloud version Docker image," was introduced, allowing users to define their installation packages. This innovation not only enhanced the abstraction levels but also offered unprecedented flexibility, reinforcing Sealos' position as a frontrunner in [cloud operating system](https://sealos.io)s. - -``` -dockerfile -FROM kubernetes:v1.25.0 -COPY mysql . -CMD helm install mysql . -``` - -This concept allows the [cloud operating system](https://sealos.io) to have "images" just like a standalone operating system. Another step is completed in this grand vision. - -## Sealos as a [cloud operating system](https://sealos.io) - -At its core, Sealos is more than just a set of tools; it's a full-fledged [cloud operating system](https://sealos.io). With the mantra "Everything is an application," Sealos redefines how we perceive data centers and cloud resources. By treating the entire data center as a unified entity rather than isolated servers, Sealos turns it into a virtual supercomputer, offering seamless, distributed application management. This approach not only simplifies the cloud computing process but also maximizes efficiency and scalability. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-08-21-15-30-Zn4l1W.webp) - -## Design Philosophy: Minimalism and User-Centric Approach - -In a domain often cluttered with complexity, Sealos stands out with its minimalist design. But don't be fooled by its simplicity; every element of Sealos is designed with powerful functionality in mind. This design philosophy stems from a deep understanding of user needs, especially in the B2B software realm where user experience is often overlooked. Sealos breaks this norm, ensuring that every interaction with the platform is intuitive, efficient, and pleasant. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-08-21-15-31-jqkByJ.png) - -The black, white, and gray design style will make you feel like you're drinking plain water while using the product, rather than a beverage, let alone footwash (as some products make you feel like dying). Developers already suffer enough, and I hope that using Sealos will bring you a pleasant mood. - -Sealos can pinpoint the pain points of applications. For example, the App Launchpad, an application manager, allows you to launch your own application within 30 seconds. This involves numerous details, such as automatically configuring public domain names and resolving HTTPS certificate issues. - -## Affordability and Efficiency with Sealos - -One of the most compelling aspects of Sealos is its cost-effectiveness. The platform allows for the efficient running of applications, significantly reducing operational costs. This affordability is achieved through innovative approaches like paying only for active containers and automatic scaling during low traffic periods. Such features make Sealos not just a powerful [cloud operating system](https://sealos.io), but also an economically viable solution for businesses of all sizes. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-08-21-15-34-NmR7oB.png) - -For enterprises, this can significantly reduce resource utilization costs. We ourselves run over 7,000 applications on just 10 servers. What does that mean? After deploying a Sealos cluster, as long as the server resource utilization is below 70%, you can continuously add applications to the cluster until it reaches its capacity. - -You might wonder, **why not use Kubernetes directly?** The reason is simple. For enterprises like Xunfei, applications are distributed across various departments, making multi-tenancy, isolation, and collaboration crucial. Using Kubernetes directly could disrupt the cluster, and the worst-case scenario is that a department or user inadvertently causes a security issue that crashes the entire cluster. Sealos perfectly solves this problem! - -Sealos can help 80% of enterprises reduce their resource utilization costs by 80%. - -## Sealos: Liberating Cloud Management - -Sealos champions the principle of "everything is an application," catering to a diverse range of users, from novices to cloud-native experts. This design ensures that users can leverage Sealos without the burden of understanding complex Kubernetes concepts. At the same time, it provides flexibility and power to those who are well-versed in cloud-native technologies. This dual approach democratizes cloud management, making it accessible to a broader audience while retaining the depth and flexibility for experts. - -Sealos pays great attention to the coordination between applications. For example, if you're using function compute on Sealos, the default database might be MongoDB. But what if you want to use PostgreSQL? In this case, you can install a PostgreSQL application on Sealos and access it directly within the function compute through service discovery. Since they are in the same cluster, they can directly communicate through internal DNS. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-08-21-15-37-5VOWuC.png) - -Sealos is streamlined yet not simplistic. All components can be uninstalled, allowing the cloud to perfectly meet your needs—more is considered excessive, less is considered insufficient. This also means that whether it's a single server or hundreds of data centers, they can be built into a cloud with just one command. - -## Real-World Applications and User Base - -- Run an nginx demo on Sealos in just 30 seconds with automatic scaling. -- Start various databases in 30 seconds and connect to them directly within your business system's intranet. -- Launch your business applications written in various programming languages directly on Sealos. - -These three capabilities serve as the foundation, and you can gradually explore and discover new territories. - -When it comes to running your own business, we have made many detailed optimizations for this scenario. For example, automatic allocation of subdomains, horizontal autoscaling, and support for running various stateful services. - -You will find that with Sealos, **whether you're deploying a monitoring system or running a low-code platform, it's all within reach. You can easily host your blog on Sealos at a low cost. Using the Sealos terminal, you can run any Kubernetes-compatible application without difficulty in automation.** - -Sealos' versatility is demonstrated through its wide array of applications across various sectors. From supporting high-concurrency services during critical times to running large-scale GPU clusters, Sealos proves its mettle in the most demanding scenarios. Its growing community, with over 100,000 users, including large enterprises, is a testament to its reliability and efficiency in real-world applications. - -![Sealos Community](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-19-51-Ve2aWX.png) - -## The Future Roadmap of Sealos - -Looking ahead, Sealos is poised for even greater achievements. The roadmap envisions a ubiquitous [cloud operating system](https://sealos.io), offering an experience as seamless and user-friendly as a personal computer. With a commitment to continuous innovation, Sealos aims to enable rapid deployment of new businesses, significant cost reductions, and the simplicity of creating a cloud with just a click. - -The Sealos [cloud operating system](https://sealos.io) will also incorporate a **Copilot**, acting as a navigator's assistant. It can automatically perform cloud-native transformations, helping developers easily enter the realm of cloud-native. It can also assist in diagnosing cluster issues, identifying security vulnerabilities, and providing professional operational advice like an expert. - -## Conclusion - -Reflecting on the five-year journey of Sealos, it's evident that the vision conceived at the first line of code has been realized. This journey, marked by milestones, challenges, and triumphs, was made possible by the trust and support of the community, contributors, and partners. As Sealos embarks on its next phase, it stands ready to exceed expectations and redefine the cloud computing experience. - -Experience the transformative power of the [Sealos cloud operating system](https://cloud.sealos.io). Join our community and embark on a journey of simplified, efficient, and innovative cloud computing. \ No newline at end of file diff --git a/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/feature.jpg b/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/feature.jpg deleted file mode 100644 index 47e90ce0728..00000000000 Binary files a/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/feature.jpg and /dev/null differ diff --git a/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/sealos-database.png b/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/sealos-database.png deleted file mode 100644 index f51861d140c..00000000000 Binary files a/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/sealos-database.png and /dev/null differ diff --git a/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/index.md b/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/index.md deleted file mode 100644 index e605d45b8b4..00000000000 --- a/docs/blog/en/2023/to-run-or-not-to-run-a-database-on-kubernetes/index.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -slug: to-run-or-not-to-run-a-database-on-kubernetes -title: To run or not to run a database on Kubernetes? -description: This article thoroughly examines the advantages of using Kubernetes (K8s) for database management, including enhanced stability, performance, and operational efficiency. -authors: [fanux] -tags: [Kubernetes, Sealos, Database] -keywords: [Cloud Operating System, Sealos, K8s, Cloud Native, Cloud Computing, Cloud OS, PaaS, Database, Container] -image: ./images/feature.jpg -date: 2023-12-06T10:00 ---- - -Yesterday, an insightful article by Mr. Feng explored [the drawbacks of deploying databases in K8S](https://mp.weixin.qq.com/s/4a8Qy4O80xqsnytC4l9lRg). - -Debating the merits of containerized databases might have been relevant four years ago, but in 2023, it's crucial to understand the broader acceptance of this technology. - - - -I began working with K8s from its 0.9 version. Initially, with the CSI still in its nascent stage, true stability wasn't achieved until version 1.0. During my time at iFLYTEK, I played a key role in developing and maintaining a comprehensive system that was integral to the company's internal PaaS services. - -We set up a cluster with 30 physical machines, deceptively small yet technologically advanced, running around 3000 applications of varied types including microservices, databases, message queues, and caches. **This cluster, utilized by hundreds of our developers, was maintained with less than half a person's effort, illustrating the efficiency brought by K8s.** - -Additionally, we managed to upgrade the Linux kernel seamlessly, a task unimaginable without the support of K8s. Normally, coordinating such an upgrade could take as long as six months. - -I've also seen a cluster hosting 400 databases, which required 400 servers and a 40-person team, yet operated at less than 10% efficiency. This cluster, a victim of excessive manual maintenance, reflects a common challenge many teams encounter in managing and optimizing their infrastructure efficiently. - -Upon joining Alibaba, I observed that all databases for delivery purposes were operated on K8s. For over five years now, we've been running databases in containers flawlessly, with no incidents. - -## Democratizing Database Expertise on K8s - -In the business sector, companies often confront two primary challenges with database management: either their management skills are not robust enough to maximize database potential, or they face significant expenditures in managing their databases. The concept of "[Database on K8s](https://sealos.io/docs/guides/dbprovider/)" introduces a standardization that addresses these issues. Such standardization facilitates collaboration and alters the dynamics of productivity and production relationships, leading to a substantial increase in efficiency. This approach empowers teams, even those lacking in specialized skills, to leverage professional capabilities. This is analogous to the distinct roles in agriculture and animal husbandry, where focus in their respective domains enhances overall efficiency and output. - -The KubeBlocks team is a prime example of this. Their expertise and accumulation of knowledge in database management are likely superior to that of most companies. They have transformed their experiential knowledge into code, crafting controllers that enable other businesses to operate in an exceptionally streamlined manner. K8s plays a crucial role in making this feasible. - -One common query is: Why not opt for Ansible? Operational staff often favor Ansible due to its compatibility with their tools. However, Ansible is primarily focused on deployment and operational tasks. K8s controllers, on the other hand, are built on the principle that **tasks achievable by machines should not be manually performed by humans**. They facilitate **a constant, 24-hour synchronization between desired and actual states**, a challenging task with Ansible. Would Ansible be the choice for setting up a routine task? - -This mirrors the era before operating systems when programmers manually punched holes in paper tapes to execute programs. Running programs on paper tapes or CDs was possible, but it begged the question of the necessity of operating systems. - -The underlying principle is the same: Ansible is a valuable tool for operational staff, but K8s's objective is to do away with lower-level operational work (such as writing and executing Ansible scripts). K8s enables more efficient and automated database management, granting teams without extensive database management expertise access to high-level services. - -## The Benefits of Running Databases on Kubernetes - -Most concerns around database-on-Kubernetes boil down to: - -**How stable is it?** - -**Can I effectively troubleshoot issues?** - -**Does performance suffice?** - -### Complexity - -Complexity for databases on Kubernetes involves two key aspects: - -1. Build complexity -2. Usage complexity - -**First: Build Complexity** - -Building a production-grade database platform directly atop native Kubernetes proves costly, unfriendly for beginners lacking depth of expertise. You'd need to create multiple critical components yourself - Kubernetes storage drivers, database controllers, etc. Hands-on expertise makes this possible, but proves challenging. - -Hence the appeal of distributions, much like CentOS, Ubuntu, etc. for Linux instead of direct kernel wrangling. Consider Kubernetes the "cloud kernel." Using the raw kernel sans customization leaves an insufficiently robust, user-friendly experience. The kernel merely provides a framework; users must architect and integrate many optimizations themselves. Kubernetes distributions help solve this. For instance, [Sealos delivers](https://sealos.io/docs/self-hosting/lifecycle-management/quick-start/deploy-kubernetes/) **full systems including highly available clusters, finely-tuned storage, and optimized databases in one click**. Two simple commands: - -```bash -$ sealos run labring/kubernetes:v1.27.7 labring/helm:v3.9.4 labring/cilium:v1.13.4 \ ---masters 192.168.64.2,192.168.64.22,192.168.64.20 \ ---nodes 192.168.64.21,192.168.64.19 -p [your-ssh-passwd] - -$ sealos run labring/openebs:v3.9.0 labring/mysql:8.0 -``` - -Done. The complete system with HA cluster, finely-tuned storage, and optimized databases emerges in minutes. While Ansible aids installs, **it cannot handle critical runtime needs like self-healing and multi-tenancy** - key advantages Kubernetes provides for databases-as-a-service. - -**Second: Usage Complexity** - -Leveraging cloud operating system distributions and controllers, users can deploy standardized database services, moving away from script-based solutions. - -![](images/sealos-database.png) - -This interface is designed for universal ease of use. Even beginners can manage to establish a three-replica PostgreSQL cluster, incorporating backup, recovery, and monitoring functions. This capability democratizes access for all developers within an organization and **underscores the fundamental divergence between 'cloud computing thinking' and 'script-based thinking'. Cloud computing democratizes service provision (as a Service), in contrast to the traditional script-based methods which serve more as operational conveniences.** - -### Stability - -Our team, despite not specializing in database technology, has successfully established a highly stable database system. This demonstrates the potential of what dedicated experts in the field can accomplish. For users, this means that database stability concerns can be confidently entrusted to seasoned professionals. - -Consider the [Sealos Public Cloud](https://cloud.sealos.io) as an example. It currently supports thousands of applications with fully containerized databases, all under the maintenance of the KubeBlocks team. Any arising database issues are efficiently handled by them. From a cost-benefit perspective, utilizing KubeBlocks' commercial services is more economical than employing a full-time database administrator. Furthermore, as Sealos architects, we ensure that database users face minimal operational concerns. Our stability standards exceed those of many non-specialist teams. - -Additionally, database lifecycle management involves specific tasks, and over time, stability issues are progressively resolved. These improvements, often made at the code level, incrementally reduce end-user concerns. This parallels the enhanced stability of the Linux system, achieved through ongoing technological development and optimization. **A well-designed software architecture not only improves but also consolidates its robustness, reducing dependence on human oversight. An illustrative comparison is that Oracle users might find themselves enjoying more leisure time than those using open-source MySQL.** - -Hence, both empirical evidence and theoretical considerations suggest that stability should not pose a hurdle for running databases on K8s. **Choosing K8s for database management effectively means leveraging the pooled expertise of numerous preeminent database experts. Their extensive knowledge and skill, embedded in the code, provide standardized, high-quality services to users. Such depth and efficiency of expertise are challenging to replicate with basic scripting alone.** - -### Performance - -It's a common belief that databases in containers underperform, but this is usually due to a lack of proper handling. The KubeBlocks team's in-depth testing and optimization, detailed in their analysis, show that these complexities are not for users to worry about. **The intricacies are already incorporated into the controller's code, making the process user-friendly**. In fact, the effect of containers on database performance is minor, with disk I/O and network bandwidth latency being the real influential factors. - -The OpenEBS raw disk plus database controller solution addresses these performance issues effectively. By employing a database controller, dependency on distributed storage is removed. This controller ensures both high performance and availability for database replicas, irrespective of the service type, and is seamless for the user. In case of a failure, it automatically makes adjustments, offering a superior database user experience. - -[Sealos](https://sealos.io) is a prime example of this solution in action, achieving high availability without compromising performance. It interacts directly with raw disks, facilitating automatic scaling, backup, and recovery. In the event of a node failure, the controller swiftly launches a new node, synchronizes the data, and integrates it into the cluster. These advanced capabilities, beyond the reach of traditional scripting methods that often require manual intervention, showcase the superiority of cloud operating systems. - -Thus, **running databases on K8s not only avoids performance issues but also offers stability that often surpasses the capabilities of many IT operations teams**. This approach is user-friendly and straightforward, with self-service functionalities. Would this be an option you'd consider? - - -## Do not deny or affirm without considering practical scenarios - -In considering whether to containerize databases, we must take into account diverse real-world applications. - -For some companies with stable non-containerized databases and sufficient funds for database experts, there's little motivation to migrate to Kubernetes (K8s). Why risk migration issues? For example, banks often rely on specialized Oracle machines with straightforward subscription models, providing little impetus for change. - -Conversely, many business development teams and organizations now face a compelling choice: **to access sophisticated database capabilities at a minimal cost, thereby focusing their primary efforts on business development.** - -They might choose cloud database services like RDS (Relational Database Service) or Kubernetes (K8s) based database solutions. This method requires an ongoing management process, replacing manual roles and equipping teams with limited database knowledge. This represents a significant trend, where upfront costs (like developing controllers) increase, but the incremental costs for each team utilizing the database substantially decrease. - -There are various approaches to achieve this, such as virtual machines or Ansible, but Kubernetes-based controllers stand out as the superior solution. Even for services offering RDS-like functionalities, the Kubernetes tech stack emerges as the optimal choice. Virtual machines, being more cumbersome and costly, incur greater performance overheads. And for tools like Ansible to achieve self-service and multi-tenancy seems overly optimistic. - -## Summary - -### Kubernetes' Unique Strategic Significance - -Kubernetes brings tremendous force multiplication, like mastering lifelong martial arts training. Without Kubernetes, you might exert 10% of a database's true potential. Used skillfully, Kubernetes massively amplifies operational database efficiency, savings, and resilience. - -### Technological Progress Reshapes Work Patterns - -As technology advances, specialized database users and caretakers decouple. Manual upkeep yields to intelligent automation. Amidst this shift, standardization enables effective collaboration at global scale. No stronger de facto standard for cloud-native automation exists now than containers and Kubernetes, so database adoption seems inevitable. - -### Practical Proof Points - -Globally, many teams have successfully run databases on Kubernetes in production across critical dimensions like cost, usability, stability, performance, and more - with remarkable measurable results. Once accustomed to Kubernetes' advantages, tough to justify reverting to old-school manual operations. For instance, Sealos' architecture evolved from Ansible (v2) to Golang (v3) - now at [v4 and v5](https://github.com/labring/sealos). This exemplifies "cloud native thinking", not legacy "ops script thinking" tied to specific individuals. If a solution lacks even basic APIs, how can we discuss advanced scalability and productivity? Systems should consider machine consumers first, then human ones - this unlocks true automation leverage. Hence, API > CLI > GUI. - -### Operations Roles Transform - -Many legacy DBAs naturally feel inclined to spread Kubernetes database FUD, protecting their niche skillsets. But enlightened technology leadership will consistently discover immense TCO and productivity gains from thoughtful, staged standardization - we've erased entire 40-person ops teams when strategically migrating systems to Kubernetes without layoffs. Without question, many ops engineers justifiably feel their roles threatened amidst displacement by increasingly automated toolchains. But technologies only accelerate; they cannot unwind. Leaders must shepherd transitions. - -### Kubernetes Maturity and Ecosystem Growth - -Kubernetes rapidly matures while its ecosystem explodes with new solutions. Inevitable chaos results as practices and culture race to catch adoption. But time cures challenges - take heart knowing robust distributions have repeatedly emerged across domains like Linux. These tame entropy and ease adoption by curating “batteries-included” solutions optimized for regular challenges. Sealos leads here as a purpose-built Kubernetes cloud-native OS distribution for standardizing database (and other) operational burdens. Recently, across 200+ Sealos users, literally none reported fundamental database operational struggles. A few cited early instability, but root causes like resource misconfigurations got systematically addressed - now they report Kubernetes-automated databases proving >9 times more stable than DIY trial-and-error. - -### The Enterprise Strategic Choice - -Actual organizational needs should determine if and how organizations adopt Kubernetes databases-as-a-service. But the intelligent path brings tremendous advantages: solutions like Sealos + KubeBlocks equate to: - -1. 8+ years of specialized Kubernetes platform expertise -2. A top-tier database team including multiple senior engineers -3. Extreme usability, resilience, and performance - -...all for less than the cost of staffing database specialists internally. Internal political hesitations naturally arise amid shifts. But pragmatic facts speak volumes about the optimal way forward. - -### Final Thoughts - -Rather than rebutting each counterargument exhaustively, I'll leave readers to independently judge the landscape and merits based on results. Please share your perspectives so together we uncover greater collective truth. The future looks abundantly bright for Kubernetes' democratization of reliability and scale across critical domains like databases. \ No newline at end of file diff --git a/docs/blog/en/2023/what-is-sealos.md b/docs/blog/en/2023/what-is-sealos.md deleted file mode 100644 index e45fdafa791..00000000000 --- a/docs/blog/en/2023/what-is-sealos.md +++ /dev/null @@ -1,337 +0,0 @@ ---- -slug: what-is-sealos -title: "Sealos vs Traditional Cloud Platforms: A Comparative Analysis of Efficiency and Cost" -description: "Explore the innovative Sealos cloud operating system: a Kubernetes-based platform revolutionizing cloud computing. Dive into its design, unique features, and competitive advantages over traditional cloud services. Learn how Sealos optimizes user experience, reduces costs, and simplifies cloud resource management for businesses and developers." -authors: [fanux] -tags: [Kubernetes, Sealos] -keywords: [Cloud Operating System, Sealos, K8s, Cloud Native, Cloud Computing, Cloud OS, PaaS, Rancher, KubeSphere, Cloud Service] -image: https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting5@main/uPic/2023-08-31-09-52-gLmSek.jpg -date: 2023-07-10T10:00 ---- - -With the rapid development and widespread application of cloud computing, businesses and developers are increasingly seeking flexible and efficient ways to manage and deploy cloud resources. In this context, Sealos emerges as not only a [cloud operating system](https://sealos.io) with Kubernetes at its core, but also as an innovative solution aimed at simplifying and optimizing the cloud computing experience. - -This article delves into the core functions, technical features, design philosophy of Sealos, and how it revolutionizes the [cloud operating system](https://sealos.io) domain. We will also explore Sealos' applications in various usage scenarios and compare it with other cloud service platforms in the market to demonstrate its unique competitive advantages and potential. - - - -## What is Sealos? - -Conceptually, Sealos is similar to operating systems like Windows, but with two key differences. First, Sealos does not operate on a single server; its core concept is to **treat the entire data center or resources across multiple servers as a unified whole**. This approach breaks the limitation of traditional operating systems that operate on a single machine, extending resource and application management to a larger scale, **enabling the operation and management of applications across the entire data center**, significantly enhancing the utilization efficiency of cloud resources and operational capabilities. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-13-46-9Nel1a.png) - -Unlike ordinary operating systems that support daily applications like QQ and WeChat, Sealos focuses on providing developers with the environment they need for distributed applications. In the world of Sealos, **complex cloud computing tasks become as simple and intuitive as using a personal computer**. Whether running common web services like Nginx or deploying and managing distributed applications written in various programming languages, Sealos can do it all with one click, greatly reducing the complexity of configuration and management. Its design philosophy emphasizes user-friendliness and simplicity, striving to eliminate technical barriers in using cloud services, making cloud computing's powerful capabilities easily accessible to every user. - -## Core Problems Solved by Sealos - -Sealos primarily addresses the following core issues: - -### Optimizing Cloud Experience - -#### User Interface - -The significance of the User Interface (UI) is self-evident. Traditional standalone operating systems have provided us with a standardized user experience paradigm. However, many of today’s cloud platforms have evolved into vast and complex systems, causing users to lose their way in the product. This has even led to the creation of specialized training positions for cloud services, reflecting a failure in product design to some extent. For example, few people need training to use an iPhone, as its product design is already excellent and very easy to understand and operate. - -In product design philosophy, we first need to recognize that different user roles have varying focal points. The user base of cloud services includes developers, database administrators (DBAs), operations personnel, Kubernetes (k8s) experts, technical novices, and industry experts. Trying to satisfy all these roles with one product is nearly impossible. For instance, even with the same CI/CD (Continuous Integration/Continuous Deployment) tool, some prefer Jenkins, while others favor Drone. - -Many PaaS platforms take CI/CD as an example and often integrate specific tools like Jenkins, leading to a need to rebuild core functions when the tool becomes less popular or better alternatives emerge. Also, this design fails to meet the preferences of different users. - -The approach of standalone operating systems is worth learning from. The operating system itself does not interfere too much but is responsible for managing applications well. This allows users to freely choose their preferred applications, such as office software DingTalk or Feishu, which are independent of the Windows operating system. This method grants users a great degree of freedom. Although many PaaS platforms also offer app markets, they do not consider applications as the primary element. Instead, most platforms focus on Kubernetes as the core, which is not wrong per se, but **this approach only targets the cloud-native user group and fails to achieve a high level of abstraction**. - -The Sealos platform fully follows the operating system philosophy. It focuses on the specific functions users need. For example, a DBA creating a database does not need to worry about the details of Kubernetes; users of Function Compute services do not need to be concerned about whether they run in containers; and Kubernetes experts can operate through the Lens application or command-line tools. This design philosophy allows various types of users to find suitable tools and services on its platform, thereby optimizing the overall user experience. - -#### API > CLI > GUI - -Many people's understanding of a product is limited to its GUI, but in reality, a cloud service product without an API is almost useless to businesses. To improve efficiency, businesses need to connect and integrate various systems, highlighting the importance of APIs. Cloud services are often designed not just for human users but also for other programs or systems, to achieve a high degree of automation in enterprise operations. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-14-36-qnQmDa.jpg) - -Sealos provides APIs that are fully compatible with Kubernetes' CRD (Custom Resource Definitions) design. Users can manage and control their cloud resources through Sealos' APIs in the same way as operating in a Kubernetes environment. For security, Sealos assigns a permission-limited kubeconfig authentication file to each tenant. These files allow tenants to flexibly connect, manage, and secure different systems and resources. - -This design not only makes Sealos' cloud services more powerful and flexible but also provides enterprises with an efficient, automated way to manage their cloud infrastructure. With the extensive application of APIs, businesses can easily integrate Sealos cloud services into their existing workflows, thus enhancing operational efficiency and flexibility. - -#### Fast and Efficient Operation Experience - -Our goal is to ensure that most operations can be completed within 30 seconds, and at most not exceed 3 minutes. If the operation time of a feature exceeds this standard, there must be a problem, necessitating reevaluation and redesign. - -#### Catering to All Users - -Although Sealos is primarily aimed at developers, in the process of functional design, we also pay attention to the experience of non-technical background users. For this purpose, we specifically invite administrative staff without technical backgrounds to experience our services firsthand. This helps us validate the ease of use of our product. If they can smoothly complete the operation process, it proves that our product is easy to operate and user-friendly. The ease of use of the product is our core pursuit. If users need guidance from others to use the service, it indicates that our design still has room for improvement. - -#### Focus on High-Quality Applications - -In the field of application development, Sealos always prioritizes quality over quantity. For the vast majority of applications, a stable operating environment and backend database support are indispensable. We focus on refining these basic applications first, and then expand to other application fields, integrating cutting-edge applications from various directions and domains, to provide users with comprehensive and efficient solutions. - -#### Ensuring Scalability and Security - -Sealos does not set standards on its own but strictly follows mature systems and de facto standards, ensuring high compatibility with the entire cloud-native ecosystem. All cloud-native applications can run safely on Sealos, and even some non-productized applications can be run through Sealos' terminal. Our compatibility is built on full support for Kubernetes, with enhanced security measures. - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-14-48-vbJezM.png) - -In Sealos, to prevent improper operations or inappropriate image downloads from causing catastrophic effects on the entire system, each user's permissions are restricted within their own namespace. This permission management mechanism strengthens the security and stability of enterprise-level [cloud operating system](https://sealos.io)s. - -### Reducing Cloud Costs - -Our goal is not just to help you cut costs by 30%—such a target lacks challenge and is too mundane. We aim to minimize the marginal cost of cloud services, ideally to one-tenth of the original cost. How to achieve this? This is the direction we are pursuing. So, how can we achieve this goal? - -#### Redefining Cloud Architecture: Abandoning Traditional Models - -**First, we must abandon the traditional three-tier architecture model of IaaS, PaaS, and SaaS.** - -Why give up this classic architecture? The reason is that the traditional layered model no longer meets the current technological developments and market demands. Take IaaS as an example; it simulates hardware like routers, switches, and virtual machines in data centers through software. While this improves scheduling flexibility, it also leads to a sharp increase in software costs. For instance, OpenStack requires a team of dozens of people to maintain its stability, directly resulting in high software costs. In the past, this approach seemed necessary to improve resource utilization, but now, from an application perspective, many applications don't care if they run in a separate VPC during operation. - -![](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-15-00-2jC2C8.png) - -The above is a diagram I drew five years ago, which is gradually becoming a reality. This is similar to the development history of single-machine operating systems: initially layered, later evolving into a more efficient kernel architecture. The layered architecture of cloud computing also carries historical baggage. Once enterprises abandon IaaS, they can save substantial costs and enjoy higher performance. - -From this new perspective, we find that IaaS is actually unnecessary. Technically speaking, PaaS and SaaS are essentially the same; they are both application-level services and thus do not need to be overly differentiated. In the new cloud kernel architecture, we only need to effectively implement isolation between tenants. This does not require complex and heavyweight solutions. For example, Sealos offers a way to share a Kubernetes cluster among multiple tenants in an untrusted public network environment. We achieve this goal using strong isolation containers (like Firecracker), network policies (like Cilium), and block device isolation for storage (like OpenEBS), not only reducing costs but also achieving better results. - -![](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-15-02-6N4ygp.png) - -#### Increasing Application Density and Scheduling Efficiency - -Unless it's a very heavy compute-intensive application, it's almost embarrassing to not run over 100 applications on a server. On Sealos, we run 800 applications on one server while ensuring application stability. - -This is very important for enterprises, as it significantly reduces the need for hardware resources. If you are a corporate executive, you might want to re-examine your company's overall resource utilization rate, which is often below 20%. Through our method, there is much more room for improvement. - -With Sealos, enterprises can save up to half the cost in a simpler way. - -#### Full Elasticity - -Inactive applications at night should rest and leave resources for offline computing or training tasks. This is actually more advantageous with public clouds, as resources can be directly released, saving a lot of costs. - -Sealos directly incorporates this key feature. If all enterprise applications operate in this way, huge cost savings can be achieved. - -#### Eliminating Zombie Applications and Servers - -There are many development and testing programs in enterprises. How do you know which ones are not being used? There are also many zombie "servers." Some companies can only maintain who used what with an Excel sheet, asking around periodically to retire servers that no one claims. Slightly more advanced ones might use an outdated CMDB. - -The radical solution to this problem is: charging money. Yes, internal enterprise applications should also be charged, and those that owe fees should be shut down directly. - -This way, each department can apply for a budget, and developers apply for a budget. Once the budget is used up, the application is shut down, ensuring no zombie applications in the long term. Sealos brings all servers under unified management, turning the entire cluster into a large resource pool, eliminating the possibility of zombie servers. It also saves enterprises a lot of operational manpower. - -To eliminate resource waste, such fine-tuned operations are needed, and Sealos achieves this purpose at a very low cost. The only thing enterprise managers need to do is allocate money to each sub-account. - -![](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-18-ijXZ4y.png) - -This way, you can precisely control how much each department and each person spends, further analyzing ROI. - -#### Operating the Entire Cloud with Half a Person - -A team of operators? An operations team for each business unit? Have you ever seen Microsoft sell a PC and then provide you with an operator? Therefore, it's still about inadequate software. If the software is sufficiently excellent and stable, there's no need for the role of an operator. Or this role will change, like writing orchestration files or operators. - -The developers of Sealos spent less than half the effort to maintain the entire cloud. Whether it's 8,000, 80,000, 800,000, or 8 million applications, it only requires half a person. This is the [cloud operating system](https://sealos.io), which doesn't increase operational complexity as the scale grows. - -I am a neutral person, but I have an extreme view: in a sufficiently mature cloud, there should be no role for operations. If your enterprise has more than 3 operators (excluding those who move servers), you should seriously reflect on this. - -Here's a clear path for current operations staff: develop [cloud operating system](https://sealos.io)s. - -#### Research and Development Human Resource Costs - -I am a developer, and I spend at least 50% of my effort on things other than development. Those miscellaneous tasks may account for 20%, but their impact is 80%. They interrupt what I'm doing, like when you finish writing code and think about selling servers, configuring certificates, packaging, and going live. I bet no developer likes doing these things unless they're masochistic. Developers are lazy, and to be lazy, they develop a bunch of tools. This is the victory of the lazy, and Sealos is also created by the lazy. So if something can be automated, never do it manually; if AI can do it, never do it manually. - -![](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-18-as6HSI.png) - -Analyzing issues yourself is tiring; AI is more professional than humans. - -The function computing capability of Laf on Sealos makes writing code as simple as writing a blog post. Just click save, shut down, and leave. To leave work early, use Laf efficiently. - -All backend dependencies, like databases, can be resolved in under 30 seconds. In the future, AI will automatically package, deploy, code, and debug. - -This intangible increase in development efficiency can save unimaginable costs. In our customer examples, there are many cases of two people doing the work of five. - -### One-Click Private Cloud Construction, Consistent Experience Between Public and Private Clouds - -Sealos has a profound understanding of cloud computing: - -**Public and private clouds are the same thing, the same abstraction, the same set of codes, and the same experience, just with different applications installed.** - -You will find that Linux, whether running in your own data center or in a public cloud, is the same product form. This is the characteristic of excellent software: achieving a high degree of abstraction. - -Sealos was designed with this in mind. In fact, public and private clouds are essentially the same; they both link computing resources. Many people might think they are different, but public clouds also have recharge and billing. These functions just need to be placed in a separate application, which can be left uninstalled in scenarios where they are not needed. - -![](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-18-vMGZyu.png) - -However, even if a larger enterprise builds a private cloud, it should be similar to a public cloud in form. Metering and billing are very important features. Enterprises with more than 10 people need to finely operate cloud resources, let alone enterprises with thousands of employees using private clouds. The cost allocation among departments is essential. - -Some minor differences, like WeChat payment or third-party login, may indeed be unnecessary, which are just small configurations. - -## An In-Depth Look into Sealos Technology - -Sealos takes on a challenging scenario: allowing multiple tenants to share a Kubernetes cluster in an untrustworthy public network environment. - -The benefits of this approach are significant: -- Users can start directly without building a cluster and only pay for containers, significantly reducing costs. -- As the scale increases, a flywheel effect occurs, drastically reducing marginal costs. (Sealos's Moore's Law: Every doubling of the Sealos cluster size reduces cloud costs for users by 30%) - -However, this approach also presents a significant technical challenge: ensuring isolation, security, and scalability. - -By overcoming these technical challenges, we not only provide great value to customers in the public cloud but also easily handle private cloud scenarios. - -![Sealos Technology Analysis](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-19-sJYA9X.png) - -Decomposing Sealos's technology system: - -### Ubiquitous Operation -A well-known open-source project in this area might be Terraform. Unfortunately, it's slow to start when interfacing with certain clouds (due to poorly written drivers; not Terraform's fault) and can easily hit API call limits of cloud providers (also due to erratic driver requests), failing to meet our needs. - -Moreover, we prefer Kubernetes CRD standards over Terraform. Consequently, we developed our own infrastructure controller. What normally took 10 minutes to launch, we optimized to 30 seconds, which is nearly the limit barring faster server startups by cloud providers. The optimization mainly involved parallel processing and algorithmic adjustments, rather than simple 10-second delays. Additionally, launching a Sealos cluster on these VMs takes only 3 minutes, already outperforming many similar products (which generally take 15 minutes). - -Running on bare metal also requires considering extensive compatibility issues. Thus, Sealos almost entirely abandons RPM, Apt, and other OS-tied installation tools, achieving compatibility with all major Linux distributions. We don't support Windows, simply because we don't prefer it. - -Our cluster imaging capability also effectively supports mainstream hardware architectures like ARM and x86. - -### Cloud Driver Layer -This component faces massive challenges. Without considering isolation and security, installing containerd, Calico, and OpenEBS might suffice. However, in a public, untrusted environment, such weak isolation is unacceptable. Therefore, we've innovated with new technologies, such as Firecracker, for strong container isolation. The challenges of running VMs within VMs in cloud providers' infrastructure will be addressed in a separate article. - -Our network needs require high measurement and isolation standards. Traditional solutions like Calico rely heavily on iptables rules, becoming unusable at scale. We tested and found a 30% failure rate under stress with just 5000 rules. For networking, we introduced Cilium, using eBPF to address these issues and multi-tenant network metering. - -![Cloud Driver Layer](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-19-REW9Fy.png) - -For storage, we use OpenEBS plus LVM, mounting isolated volumes for each user, allowing them to enjoy local disk performance. File storage, however, becomes a significant issue. NFS and similar solutions are nearly toys, unsuitable for production. Thus, we developed Sealfs, a high-performance filesystem, from scratch in Rust, emphasizing simplicity and support for RDMA. - -### Lifecycle Management and Cluster Imaging -For cluster installation and scaling, Sealos users know the process is nearly perfected, with complex clusters managed with a single command! - -Cluster imaging capability, likely unmatched globally, is supported by Sealos and Sealer, both of which I led in development. This capability is king in delivery! Entire clusters can be packaged in a fully Docker-compatible format, enabling one-click deployment in customer environments. - -In the face of our cluster imaging capability, other delivery tools pale in comparison. - -### Tenant Management -Multi-tenancy is a critical need for any enterprise-level user. Designing tenant permissions requires flexibility without complexity, ensuring isolation and collaboration among departments or developers. Native Kubernetes doesn't address these needs, offering only rudimentary namespace management. - -Sealos assigns each user an independent kubeconfig, limiting their permissions to their namespace. Users can share their namespaces but are prohibited from risky actions like piercing through to the host, using privileges, or sharing host filesystem ports. - -### Application Management -Applications are first-class citizens in Sealos, with everything above the cloud kernel being an application. The challenge here is finding a unified application management approach in a multi-tenant environment. - -- Some applications need admin rights to run a controller. -- Others need to run a separate instance. -- Some, like a ChatGPT API, are shared by multiple users. -- Certain applications, like terminal apps, auto-release when unused. - -The system also needs to control and meter these applications, further complicating matters. - -Sealos abstracts these capabilities into applications, similar to running apps on macOS. - -### Proprietary Function Compute Application Laf - -Laf revolutionizes code writing, akin to blogging. - -- Cloud-based development with immediate results. -- Efficient use of Laf means early finishes. -- Even administrative staff can easily use cloud development. -- Function computes come in two types: those that deploy in 30 seconds, and those that discourage use in 30 seconds. - -Laf's millisecond-level publishing outperforms others, where a typical deployment takes 3 to 5 seconds. Laf achieves faster than blogging speeds. - -Laf integrates GPT-4, reducing the need for manual coding. We trained thousands of Laf codes, enhancing AI writing capabilities. - -Databases and object storage are built-in. Except for some AI coding, almost no other concerns are needed. - -It uniquely supports WebSocket, outmatching others (where function compute charges by duration). - -Unlike other function computes that are not persistent, Laf utilizes always-on, auto-scaling containers. - -### Proprietary AI Knowledge Base Application FastGPT - -Laf's AI codes, Sealos's automatic fault diagnosis, AI auto-deployment of applications, and Docker image building rely on FastGPT for knowledge base construction. - -![FastGPT Application](https://jsd.onmicrosoft.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-19-cCinq3.png) - -### Databases/Message Queues Applications -We focus on the operating system and some built-in apps. For highly specialized areas like databases and message queues, we collaborate with top teams. For databases, we partner with Kubeblocks, led by PolarDB founder Cao Wei (conveniently next door, with great coffee and reliable databases). Kubeblocks orchestrates MySQL, PostgreSQL, MongoDB, Redis, etc., ensuring data security and recovery. - -For message queues, we collaborate with RocketMQ founder Wang Xiaorui, addressing RocketMQ and Kafka services. - -In DevOps, we work with Gitea, 90% compatible with GitHub Actions, led by the Gitea creator. The near-perfect compatibility with GitHub Actions is a wise choice. - -### Metering and Billing - -Metering is complex, requiring a monitoring-like collection mechanism. Beyond measuring container CPU, memory, and disk usage, it must associate data with namespaces and ultimately accounts. More challenging measurements include database access counts in function computes and storage used by each tenant. The most difficult is measuring network bandwidth without impacting performance. - -The metering system's challenge is its sensitivity. Any error affects customer billing, necessitating a reconciliation system to verify measurements and ensure billing accuracy. - -With this capability, enterprises can finely operate internal departments and developers, automatically shutting down applications with overdue payments, virtually eliminating idle processes. - -We've also unified the abstraction for metering in both public and private cloud modes. - -## Sealos Design Philosophy and Principles - -The essence of remarkable technology is its simplicity from the user's perspective, similar to the Apple iPhone and the Android operating system. Cloud computing can achieve this too, but simplicity without powerful functionality is merely rudimentary. A brilliant architecture doesn't sacrifice complexity for functionality. - -If Sealos deviates from these principles, it becomes cumbersome and heads towards obsolescence. - -### The Principle of Simplicity - -Sealos embodies simplicity in two aspects: product design and system architecture. - -In terms of product design, we are committed to not burdening users. We want users to use the cloud as easily as they use personal computers, focusing on applications relevant to their roles without being disturbed by unnecessary features. This does not imply weak functionalities; Sealos can enhance system capabilities by extending any application and provides native APIs for flexible expansion. - -At the system architecture level, abandoning the traditional IaaS, PaaS, SaaS three-tier architecture is wise. From an application perspective, there's no need for such complex support structures. With cloud kernel and cloud drivers, **everything above the system is an application.** - -### Fragmentation - -Sealos can be simplified to just a bare Kubernetes, or expand to thousands of applications. It can run on a TV box or in data centers with tens of thousands of servers. Think of Linux, which can operate on embedded devices and in the largest data centers. This represents a fragmented architecture, meeting your exact needs without overwhelming you with unnecessary capabilities. - -Only this architecture can achieve limitless expansion. - -### Modular Assembly - -A cohesive and streamlined architecture allows for customization according to individual needs. Your cloud should be just right – neither too much nor too little. This is possible thanks to a high level of abstraction. Specific functionalities are implemented by applications themselves, while the cloud OS only needs to pool resources and manage applications. Thus, even if there are tens of thousands of applications in the ecosystem, the complexity of your cloud doesn't increase. Think about how you use a smartphone; the cloud can be used in the same way. - -## Use Cases - -### Using Sealos Cloud Services Directly - -- Any business component that can be built into a Docker image can easily run on Sealos (with future AI assistance for image building). This includes projects written in various programming languages within a company. -- One-click launch for highly available clusters of the four major databases: pgsql, mysql, mongo, and redis. Complete with backup, recovery, monitoring, and control. -- Various well-known open-source projects can run on Sealos. - -Thus, Sealos effectively supports your business systems, addressing runtime issues and all backend dependencies. - -### Building a Complete Private Cloud - -It's increasingly apparent that bare metal not only performs better than virtual machines but is also more cost-effective. However, many companies are hesitant to opt for hardware hosting due to the complexity of managing software – be it OpenStack or Kubernetes. - -The cloud service version of Sealos can be cloned exactly into your own data center. Sealos already serves tens of thousands of online users, supporting scenarios and complexities that surpass most companies. After all, companies with tens of thousands of developers are rare. - -So, with bare metal and Sealos, both hardware and software are set, and building a private cloud becomes feasible. - -The cost of self-building? - -- Purchase servers. -- Launch clusters with a single command – so simple anyone can do it, regardless of the number of servers. -- Minimal maintenance, about 0.5 person. We've nearly achieved self-maintenance. The current online cluster serves tens of thousands of applications with approximately 0.1 person's effort in maintenance. - -## Comparison with Other Platforms - -### Comparing with Other Cloud-Native PaaS Platforms - -One of the most frequently asked questions is about the biggest difference in design philosophy. Sealos does not aim to be an all-encompassing PaaS platform. The [cloud operating system](https://sealos.io) itself is highly abstracted, essentially 'nothing'. On Sealos, applications are the prime focus, meeting users' needs through various applications. For instance, when using the Database application on Sealos, you need not worry about any other concepts, not even knowing how to spell 'Kubernetes'. - -Sealos, differing from traditional PaaS platforms that aim for all-inclusiveness, regards the [cloud operating system](https://sealos.io) as a highly abstract 'nothing', emphasizing the importance of applications. On the Sealos platform, **applications are considered the top priority**, catering to a diverse range of user needs. For example, when using the database application on Sealos, users don’t need to concern themselves with any other concepts, **not even how to spell “Kubernetes”**. - -Rancher and KubeSphere are excellent PaaS platforms. However, Sealos does not consider Kubernetes as its core purpose. It focuses more on the applications running on Kubernetes than Kubernetes itself. Hence, Sealos targets a wide range of developers, intending to create a versatile operating system, not confined to serving only the cloud-native sector. Sealos even prefers not to emphasize the yet-to-be-defined concept of 'cloud-native'. - -Therefore, the core idea of Sealos is not “a better Kubernetes” but rather “**providing the applications users need through Kubernetes**”. - -> What do users really need? -> - -In the operating system domain, user needs define the system’s functionalities. The flexibility of an operating system means it does not impose extra burdens on users. For example, Windows is a gaming platform for gamers, a programming tool for programmers, and a graphic processing software for graphic artists. The identity of an operating system is determined by its users, depending on which applications are loaded. Sealos also embraces this philosophy, hence different users will have distinctly different experiences. - -### Comparing Various K8s Installation Tools - -Installation is merely a boot function of the entire operating system, but Sealos also has unique features in cluster lifecycle management and application packaging and delivery. - -Firstly, Sealos can complete installation with a single command. Secondly, using cluster images, the entire cluster can be packaged and delivered anywhere. Lastly, Sealos allows users to flexibly customize their required cluster like writing a Dockerfile, freely assembling and replacing components in the image, offering hundreds of components for user selection. - -## Conclusion - -The Sealos community now has a vast user base, developed over many years, battle-tested, with stability proven in various extreme scenarios, steady as an old dog. - -Our cloud service has seen exaggerated growth in registered users and application numbers, exceeding 6k online developers and nearly ten thousand applications within two weeks of launch. - -We will provide users with a [cloud operating system](https://sealos.io) that is consistent in both public and private cloud experiences, simple, affordable, open, and powerful. \ No newline at end of file diff --git a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/feature.jpg b/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/feature.jpg deleted file mode 100644 index ac9764aaf63..00000000000 Binary files a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/feature.jpg and /dev/null differ diff --git a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop-2.jpg b/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop-2.jpg deleted file mode 100644 index 20166e44d87..00000000000 Binary files a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop-2.jpg and /dev/null differ diff --git a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop.jpg b/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop.jpg deleted file mode 100644 index 14cb1053c6e..00000000000 Binary files a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop.jpg and /dev/null differ diff --git a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-private-address-on-sealos.jpg b/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-private-address-on-sealos.jpg deleted file mode 100644 index 60025ff8046..00000000000 Binary files a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-private-address-on-sealos.jpg and /dev/null differ diff --git a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/open-meilisearch-terminal-on-sealos.jpg b/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/open-meilisearch-terminal-on-sealos.jpg deleted file mode 100644 index 7a224efb3e7..00000000000 Binary files a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/open-meilisearch-terminal-on-sealos.jpg and /dev/null differ diff --git a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/star-history-of-meilisearch.png b/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/star-history-of-meilisearch.png deleted file mode 100644 index df050313a35..00000000000 Binary files a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/star-history-of-meilisearch.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/index.md b/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/index.md deleted file mode 100644 index 9aab30abd4e..00000000000 --- a/docs/blog/en/2024/how-to-deploy-and-configure-meilisearch-using-docker/index.md +++ /dev/null @@ -1,280 +0,0 @@ ---- -slug: how-to-deploy-and-configure-meilisearch-using-docker -title: "How To Deploy and Configure Meilisearch using Docker" -description: Discover how Meilisearch, the lightning-fast open-source search engine, can transform your app's search experience. Learn installation, features, and integration tips. -authors: [Carson Yang] -tags: [Developer Tools, Sealos, Meilisearch, Flarum] -keywords: [Meilisearch, search engine, fast search, open-source, Flarum, Meilisearch vs Elasticsearch, search engine comparison] -image: ./images/feature.jpg -date: 2024-07-01T10:00 ---- - -## Introduction - -In today's digital world, search functionality is a real game-changer. It's the backbone of virtually every application, making our lives easier and more efficient. From e-commerce platforms to content management systems and corporate knowledge bases, users want to find information quickly and accurately. However, traditional search solutions often fall short, struggling with slow response times, irrelevant results, poor scalability, and lack of flexibility. These issues don't just frustrate users – they can drive them away, ultimately putting a dent in business growth. - -We're so excited to introduce you to [Meilisearch](https://www.meilisearch.com/), an amazing open-source search engine that's here to shake things up! Meilisearch is a fresh newcomer to the search engine scene, and it's already making waves with its easy-to-use deployment, lightning-fast queries, and feature-rich toolkit. Just imagine this: with just one simple command, you can have a Meilisearch server up and running, ready to tackle queries. It's not just powerful; it's also super user-friendly! It's got all these amazing features like fuzzy matching and schema-less indexing, plus a sleek web interface for you to play around with. - - - -In this guide, we're going to take a deep dive into Meilisearch's world. Here's what we'll cover: - -+ the nitty-gritty of Meilisearch's standout features and why they matter. -+ setting up Meilisearch on your system step by step. -+ Meilisearch 101: Essential techniques to get you started. -+ Real-world application: Integrating Meilisearch into a Flarum forum (because theory is great, but practice makes perfect). -+ How Meilisearch stacks up against the competition in the search engine arena. - -## Prerequisites - -Before we get started, we just wanted to make sure you've got these bases covered: - -- You're comfortable navigating the command line -- You've got a grasp on RESTful API concepts -- Bonus points: A [Sealos](https://sealos.io) account for lightning-fast Meilisearch deployment (optional, but handy) -- Ready to revolutionize your search experience? Let's get started with Meilisearch! - -## Introduction to Meilisearch - -[Meilisearch](https://github.com/meilisearch/meilisearch) is a game-changing open-source search engine, it is powered by [Rust](https://www.rust-lang.org/) and offers lightning-fast full-text search capabilities while being incredibly user-friendly and easy to integrate. Meilisearch is built on four key principles that set it apart: - -1. **Blazing Speed**: It delivers results in under 50 milliseconds, no matter how large your dataset is. -2. **Spot-On Relevance**: It uses smart algorithms to ensure the most relevant results always come first. -3. **Developer's Dream**: It features intuitive APIs and clear documentation, making integration a breeze. -4. **Tailor-Made Searches**: It offers a wide range of customization options to fine-tune your search experience. - -Since its launch in 2018, Meilisearch has been on a roll! It's been a real rising star in the open-source community. Thanks to its user-friendly features and exceptional performance, it has already garnered an impressive 40,000+ stars on GitHub. - -![Star history chart of the Meilisearch open source project](./images/star-history-of-meilisearch.png) - -## ## Core Features of Meilisearch - -Meilisearch offers a robust set of search capabilities, making it an ideal choice for projects ranging from personal websites to large-scale enterprise applications. Here's what sets it apart: - -### Lightning-Fast Performance - -- **Blazing Speed**: It delivers results in under 50 ms, no matter how much data you're working with. -- **Instant Feedback**: It even implements search-as-you-type for real-time responsiveness! -- **Typo-Tolerant**: It intelligently handles misspellings, ensuring relevant results despite query errors. - -### Advanced Relevance Features - -- **Custom Ranking**: Tailor result prioritization to your specific needs. -- **Faceted Search**: Enable intuitive filtering and navigation through multi-dimensional data. -- **Synonym Recognition**: Enhance search flexibility by setting up synonyms. - -### Comprehensive Language Support - -- **Global Readiness**: Optimized for a wide array of languages, including complex scripts like Chinese and Japanese. -- **Smart Filtering**: Customizable stop word lists to improve result quality across all supported languages. - -### Powerful Advanced Features - -- **Geo-aware Search**: Incorporate location-based searching and sorting. -- **Secure Multi-tenancy**: Implement robust data segregation with tenant tokens. -- **Result Highlighting**: Emphasize matching text in search outputs. -- **Flexible Document Management**: Easily add, update, or remove documents within your index. - -### Developer-Friendly Ecosystem - -- **Intuitive API**: Offers a RESTful interface for seamless integration. -- **Multilingual SDKs**: Official support across various programming languages. -- **Comprehensive Documentation**: Access detailed guides and practical examples. -- **Self-Hosting Option**: Deploy on your own infrastructure for complete control. - -## Meilisearch vs. Other Search Solutions - -Let's take a closer look at Meilisearch and see how it compares to other leading search solutions! - -| Feature | Meilisearch | Elasticsearch | Algolia | -| ---------------- | -------------- | ------------------------------------------ | --------- | -| Response time | Lightning-fast (<50ms) | Varies, often >100ms | Swift (<100ms) | -| Ease of use | Highly intuitive | Moderate learning curve | User-friendly | -| Typo correction | Built-in | Configurable | Built-in | -| Multilingual support | Excellent | Proficient | Excellent | -| Geospatial search | Supported | Supported | Supported | -| Open-source | Fully open | Partially (some proprietary features) | Proprietary | -| Pricing | Free (self-hosted) | Free (self-hosted), paid cloud options | Paid SaaS | - -While Elasticsearch has a ton of great features and a strong ecosystem, Algolia is all about cloud services and out-of-the-box functionality. But what really sets Meilisearch apart is its speed, simplicity, and open-source commitment. If you're a small to medium-sized project or team looking for the best of both worlds—performance and accessibility—Meilisearch is the perfect fit! - -## Installation and Configuration of Meilisearch - -Setting up Meilisearch is a piece of cake! With a smorgasbord of installation options to fit your needs like a glove, it couldn't be easier. - -One of Meilisearch's standout features is its seamless integration with Docker, making deployment and scalability a breeze. The official Meilisearch Docker image allows you to get a Meilisearch instance up and running with just a single command: - -```bash -$ docker run -p 7700:7700 getmeili/meilisearch:latest -``` - -This Docker-based approach ensures consistent environments across development, testing, and production, significantly simplifying the deployment process. - -Not a tech wizard? No problem! The [Sealos app store](https://sealos.io/docs/guides/templates/) has got your back with one-click deployment templates. It's as easy as pie - just point, click, and you're off to the races! - -**Do you Want to get Meilisearch up and running in a jiffy without getting lost in the weeds of complex setup?** Sealos might be your golden ticket. - -First things first, head over to the [Meilisearch template page](https://template.cloud.sealos.io/deploy?templateName=meilisearch) and hit the "Deploy on sealos" button in the top right corner. - -> New to the [Sealos](https://sealos.io/) scene? No biggie - just sign up for a Sealos cloud account, log in, and you'll be on your way to the deployment page in no time. - -Now, listen up! There are just two settings you need to get right to keep your Meilisearch instance running like a well-oiled machine: - -1. **MEILI_ENV**: This bad boy sets the mood for your instance - either **production** or **development**. - - Production mode: - - Search preview? Nope, it's all business. - - Development mode: - - Search preview's on tap - perfect for tinkering to your heart's content. - -2. **MEILI_MASTER_KEY**: Think of this as your Meilisearch's VIP pass. It locks down everything except `GET /health`. Want in? You'll need to flash those API keys. - - Production mode: - - A master key is non-negotiable. No ifs, ands, or buts. - - Try to skimp on the key (less than 16 bytes), and Meilisearch will throw a tantrum and refuse to play ball. - - Development mode: - - Master key? Take it or leave it. - - No key? It's the Wild West - everything's up for grabs. - -Here's the lowdown: If you don't bring your own key to the party or if it's not up to snuff, Meilisearch will try to hook you up with an auto-generated one. - -Long story short: **In production, a master key is your ride-or-die. In development? It's dealer's choice.** - -Whipping up a key is a walk in the park. Just punch this into your Linux or macOS terminal: - -```bash -openssl rand -base64 48 -``` - -Once you've done that, just hit the "Deploy Application" button and let the magic happen. - -Once everything is all said and done, just head on over to the app details page and hang tight until you see "running." Then, just give that external address a click, and you'll be in Meilisearch search preview heaven in no time! - -All you have to do is enter your master key, and you're all set! - -Here's a little pro tip for you: just give your Sealos desktop (that's the [cloud.sealos.io](https://cloud.sealos.io) page) a quick refresh, and you'll see a shiny new icon! - -![Meilisearch on Sealos Desktop](./images/meilisearch-on-sealos-desktop.jpg) - -Just one click, and you'll be right there with the Meilisearch preview! - -![Meilisearch on Sealos Desktop](./images/meilisearch-on-sealos-desktop-2.jpg) - -Does this feel a little familiar? It's got that Windows shortcut vibe going on, doesn't it? It just goes to show that Sealos can do all the same tricks as your trusty old desktop OS, but with its head in the clouds! - -## Basic Usage of Meilisearch - -Meilisearch offers a user-friendly RESTful API that seamlessly integrates with various programming languages and frameworks. Here's a quick guide to essential operations: - -### 1. Creating an Index - -Start by setting up a search index for your data: - -```bash -$ curl \ - -X POST 'http://localhost:7700/indexes' \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer YOUR_API_KEY' \ - --data-binary '{ - "uid": "movies", - "primaryKey": "id" - }' -``` - -### 2. Adding Documents - -Next, populate your index with some data: - -```bash -$ curl \ - -X POST 'http://localhost:7700/indexes/movies/documents' \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer YOUR_API_KEY' \ - --data-binary '[ - { - "id": 1, - "title": "Carol", - "genres": ["Romance", "Drama"] - }, - { - "id": 2, - "title": "Wonder Woman", - "genres": ["Action", "Adventure"] - } - ]' -``` - -### 3. Performing a Search - -Finally, execute a search query on your indexed documents: - -```bash -$ curl \ - -X POST 'http://localhost:7700/indexes/movies/search' \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer YOUR_API_KEY' \ - --data-binary '{ - "q": "wonder" - }' -``` - -## Integrating Meilisearch into Flarum Forum - -This guide walks you through integrating Meilisearch into your [Flarum](https://template.cloud.sealos.io/deploy?templateName=flarum) forum, giving your users a turbo-charged search experience. - -### 1. Install Meilisearch SDK - -Fire up the terminal from your Flarum app's details page and run. In the Flarum application details interface, click the terminal button in the bottom right corner: - -![Open Meilisearch terminal on Sealos Desktop](./images/open-meilisearch-terminal-on-sealos.jpg) - -Execute the following command in the opened terminal to install the Meilisearch SDK: - -```bash -extension require meilisearch/meilisearch-php -``` - -### 2. Add Scout Search Extension - -In the same terminal, execute: - -```bash -extension require clarkwinkelmann/flarum-ext-scout -``` - -### 3. Set Up Scout Extension - -Head over to your Flarum admin panel. Then, switch on Scout and choose Meilisearch as your search engine. Then, just plug in Meilisearch's URL and key - -If you've got Meilisearch and Flarum in the same Sealos availability zone, you can just use Meilisearch's internal network address, go ahead and enter the Meilisearch application details page. Once you're there, just click on the internal network address to copy Meilisearch's internal network address. Then, paste it as the value of the Scout plugin's Meilisearch Host above. - -![Meilisearch private address on Sealos](./images/meilisearch-private-address-on-sealos.jpg) - -### 4. Beef Up Meilisearch's Memory - -Meilisearch needs a little more space to play nice with Flarum. Here's how to bump it from 128M to 1G: - -1. Find your Meilisearch app details -2. Hit "Update" -3. Crank memory up to 1G -4. Save changes - -### 5. Feed Meilisearch Your Data - -Get your existing content into Meilisearch with: - -```bash -php flarum scout:import-all -``` - -You'll see "Imported" messages for each data type when it's done. - -And there you have it! Your Flarum forum now boasts turbocharged search capabilities, including slick Chinese text search! Let's give it a spin! - -## Conclusion - -Meilisearch is a real powerhouse for developers looking to supercharge their apps with lightning-fast, spot-on search capabilities. Our journey through this tutorial has unveiled Meilisearch's standout features, guided you through the setup process, and shown you how to weave it seamlessly into your projects. - -But hey, don't stop here! We’d love for you to explore even more of the amazing features that Meilisearch has to offer! Have fun tinkering with custom ranking rules and playing around with synonym settings! You'll be amazed at how you can fine-tune your search experience. \ No newline at end of file diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-console.png b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-console.png deleted file mode 100644 index e8295821638..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-console.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-console2.png b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-console2.png deleted file mode 100644 index dcb1c5a2143..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-console2.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-details.png b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-details.png deleted file mode 100644 index b38e1ae3e83..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-details.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-logs.png b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-logs.png deleted file mode 100644 index 82ffc311092..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-logs.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-on-sealos.png b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-on-sealos.png deleted file mode 100644 index 8647a6b6605..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-on-sealos.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-on-sealos2.png b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-on-sealos2.png deleted file mode 100644 index 061cd0eb230..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-on-sealos2.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-whiteboard.png b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-whiteboard.png deleted file mode 100644 index dcc99fe0837..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-whiteboard.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-workspace.png b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-workspace.png deleted file mode 100644 index f40ff9204f4..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE-workspace.png and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE.jpg b/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE.jpg deleted file mode 100644 index 8fea1fd20f1..00000000000 Binary files a/docs/blog/en/2024/how-to-set-up-affine/images/AFFINE.jpg and /dev/null differ diff --git a/docs/blog/en/2024/how-to-set-up-affine/index.md b/docs/blog/en/2024/how-to-set-up-affine/index.md deleted file mode 100644 index a9c5439ec00..00000000000 --- a/docs/blog/en/2024/how-to-set-up-affine/index.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -slug: how-to-set-up-affine -title: "AFFINE Deployment and Usage Guide: Mastering AFFINE's Self-Hosting Basics" -description: Master AFFiNE self-hosting and experience the future of knowledge management. Our guide takes you through the process, enabling you to harness this integrated KnowledgeOS. -authors: [Carson Yang] -tags: [Notes, Sealos, Wiki] -keywords: [AFFINE, Sealos, Notion, Notes, Wiki, whiteboard] -image: images/feature.jpg -date: 2024-04-07T10:00 ---- - -AFFiNE is a completely open-source alternative to Notion and Miro, with a greater emphasis on privacy and security. Unlike Notion, AFFiNE prioritizes the storage of note content locally. The GitHub repository can be found at [https://github.com/toeverything/AFFiNE](https://github.com/toeverything/AFFiNE). - - - -Built using Rust and TypeScript, AFFiNE offers developers maximum flexibility with a simple, single-command setup. This allows for easy customization and deployment of the application. - -While Miro and Notion focus primarily on whiteboards and pages, AFFiNE positions itself as an integrated KnowledgeOS. It supports kanban boards, tables, and rich text paragraphs as building blocks to create pages or whiteboards, enabling comprehensive document editing, data processing, and brainstorming in one place. - -![](./images/AFFINE.jpg) - -## Key Features of AFFiNE - -### 1. Seamless Integration of Documents and Whiteboards - -AFFiNE stands out as one of the few applications that allows users to place any type of content on a borderless canvas, including rich text, sticky notes, embedded web pages, multi-view databases, linked pages, and even slides. - -Each page in AFFiNE has two views, allowing users to access and edit fully functional blocks anywhere, in any form. - -![](./images/AFFINE-whiteboard.png) - -### 2. Multi-modal AI Copilot - -AFFiNE's AI capabilities can assist with a wide range of tasks, from writing professional work reports and turning outlines into expressive slides to summarizing articles into well-structured mind maps. It can even help users draw and write prototype apps and web pages with just a single prompt. - -### 3. Local-First & Real-Time Collaboration - -AFFiNE adheres to the concept of local-first, emphasizing data privacy. Users have the option to opt out of the cloud service and synchronize their data independently, providing them with greater control over their information. - -### 4. Self-Hosting - -Users can freely fork and build custom versions of AFFiNE or self-host the application. AFFiNE also plans to launch a plugin community and support third-party modules in the future. - -## Self-Hosting AFFiNE - -Self-hosting AFFiNE requires PostgreSQL and Redis databases, which can be complex to set up. However, the [Sealos app store](https://sealos.run/docs/guides/templates/) offers a one-click deployment template that simplifies the process. - -To get started, open this link: - -[![](https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://bja.sealos.run/?openapp=system-template%3FtemplateName%3Daffine) - -Set the administrator's email (AFFINE_ADMIN_EMAIL) and password (AFFINE_ADMIN_PASSWORD), then click "Go to Sealos deployment" in the upper right corner. - -> New users of [Sealos](https://sealos.io) will need to register and log in to a Sealos public cloud account. After logging in, you'll be redirected to the template deployment page. - -On the deployment page, click "Deploy Application" in the upper right corner to initiate the deployment process. Once the deployment is complete, click "Details" to view the application details page. - -![](./images/AFFINE-on-sealos.png) - -Wait for the instance status to change to "running," then click the log icon to view the logs: - -![](./images/AFFINE-on-sealos2.png) - -A successful startup will display the following log: - -![](./images/AFFINE-logs.png) - -Click on the external network address to open the visual interface of AFFiNE: - -![](./images/AFFINE-details.png) - -By default, AFFiNE uses local browser storage. To log in to your self-hosted instance, click "Log in and enable" in the upper right corner: - -![](./images/AFFINE-console.png) - -After logging in, be sure to click "Enable AFFINE Cloud Service" to avoid data loss, as failing to do so will result in data being stored in the local browser: - -![](./images/AFFINE-console2.png) - -Once the self-hosted cloud service is enabled, you can start using AFFiNE and take advantage of all its features. \ No newline at end of file diff --git a/docs/blog/en/authors.yml b/docs/blog/en/authors.yml deleted file mode 100644 index 1a9418e77f7..00000000000 --- a/docs/blog/en/authors.yml +++ /dev/null @@ -1,18 +0,0 @@ -fanux: - name: fanux - title: '@sealos' - url: https://github.com/fanux - image_url: https://avatars.githubusercontent.com/u/8912557?v=4 - -xiao-jay: - name: xiao-jay - title: '@sealos' - url: https://github.com/xiao-jay - image_url: https://avatars.githubusercontent.com/u/87080562?v=4 - -Carson Yang: - name: Carson Yang - title: '@sealos' - url: https://github.com/yangchuansheng - image_url: https://avatars.githubusercontent.com/u/15308462?v=4 - diff --git a/docs/blog/zh-Hans/2019/08/26/kubernetes-with-sealos.md b/docs/blog/zh-Hans/2019/08/26/kubernetes-with-sealos.md deleted file mode 100644 index e74aee4638d..00000000000 --- a/docs/blog/zh-Hans/2019/08/26/kubernetes-with-sealos.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -slug: kubernetes with sealos -title: sealos - 以 kubernetes 为内核的云操作系统发行版 -authors: [fanux] -tags: [kubernetes,sealos] ---- - -# 基于云内核的未来云计算架构 - -早期单机操作系统也是分层架构,后面才演化成今天的如 linux windows 的宏内核微内核架构,云操作系统也会有类似发展趋势 - -以前都是单机应用,而现代应用几乎都是分布式应用,kubernetes 已经成为事实上的“云操作系统内核”,能让云内核普及的发型版呼之欲出 - -![image](https://user-images.githubusercontent.com/8912557/176412542-2726e976-617b-4921-a37c-375a814deb8b.png) - -你会发现现在 IaaS PaaS SaaS 在云原生技术普及的浪潮中已经名存实亡,比如容器运行在裸机上就已经拥有非常好的性能了,是否还需要 IaaS 这一层,PaaS SaaS 本质都是容器是否还需要去可以区分,这三层架构已经被击穿! - -程序员很认“鸭式辩型”,就是会游泳长了翅膀的就是鸭子,这种抽象思维是极重要的,这也就是为啥 linux 一切皆文件的设计哲学了。而一个运行的 mysql 集群与一个 crm 软件其实也没有本质区别,所以在云操作系统中,“内核之上皆为应用”。 - -## 云计算三次浪潮 - -基于云内核的云操作系统未来会引发云计算的巨大变革。 - -![image](https://user-images.githubusercontent.com/8912557/176412592-51d55344-0f57-4918-8499-826fa7a7bf28.png) - -先来看看有意思的 web1 web2 web3, 再把互联网的变革套用到云计算中,你会发现,生产关系有非常类似的地方。 - -【1 对 n 关系】 -* web1 : 门户网站生产内容,用户查看内容 -* 云计算 1.0 : 公有云厂商开发服务,企业和开发者使用 - -这个阶段生产关系是 1 对多的,你会发现云厂商几十款云产品是无法满足市场上体量庞大偏好各异的需求的,就像 web1 用户只能看小编写的一些新闻一样。 - ---- -【n 对 1 对 n 关系】 -* web2 : UGC 用户生产内容,用户之间产生链接 -* 云计算 2.0 : 开发者生产云计算应用,给用户使用 - -渐渐的云厂商开始弄 markting place, 一定程度想通过开放市场来连接云计算的生产者与消费者,这就是云计算朝着 2.0 过度的信号, 但是缺乏标准就意味着难以协作,这个阶段想要彻底爆发必须要有“实际上的标准”出现。 - -Docker 镜像算是非常好的标准,但是可惜难以覆盖分布式软件,但是大家通过 docker hub 协作就是一个非常好的协作模型了。 - -kubernetes 的 API 的标准是真正有潜力成为云计算 2.0 事实标准的。未来大家都通过这个系统来相互协作,就像安卓生态蓬勃的应用爆炸一样,这样才能诞生越来越多优质的云服务出来。 - ---- -【n 对 n 关系】 -* web 3 : 网络所有权属于网络的所有参与者,数据回归用户自己手中 -* 云计算 3.0 : 算力属于所有计算的参与者,一台分布式超级计算机诞生 - -整个过程其实是让计算和服务更民主,任何组织个人都可以贡献自己的算力,发布和使用应用的人也不用关心应用到底运行在哪个地方,整个计算的使用像使用一台虚拟计算机一样。 和现在很多大的公链一样,不过目前的智能合约还是场景过于局限,计算成本过高,形式上很像超级计算机,效果上还是差了好几个鸿沟。 - -## 基于云内核设计的云计算会更便宜 - -当前公有云提供的云服务还是极其昂贵的, 在某云厂商官网查到的价格和 IDC 托管硬件相比,如果是存储类型的机器,价格相差十倍!(不过云厂商对大B都有非常大的折扣,小B没有这种福利) - -![image](https://user-images.githubusercontent.com/8912557/176412650-31db6594-a15b-4c95-a47b-9838e9fff6a1.png) - -很多公有云厂商妖魔化私有云,说私有云就不叫云,我想问私有云怎么就不叫云了,是因为私有云太便宜还是私有云动了谁的蛋糕? - -这个价格对比小学生都能算的清楚。其实在云内核设计的云操作系统出现之前公有云确实会便宜,因为软件成本很高,企业想云在自己机房玩一套如 openstack 这样的 IaaS 几乎每年会花费上千万成本,而现在开源生态逐渐成熟让软件成本变得便宜和稳定,私有云的成本便宜逻辑又开始成立了。 - -那还有个问题就是“传统公有云为什么贵?” -* 第一,因为基于的还是 IaaS PaaS SaaS 的架构,每一层都意味着成本,软件的复杂度直接决定成本,所谓的一切自研的优势现在反而会变成成本劣势,这是最主要的原因。 -* 第二,谈边际成本,这个不是按照公有云的用户体量去计算的,而是按照每个可用区的建设成本去计算的,如果软件体系复杂,每个机房需要大量管理节点,需要大量交付人员配合,那成本就无法降下来。但是基于内核设计的云操作系统管理节点只需三台,实习生都能在半个小时以内交付,就像装 centos 一样简单。 -* 第三,次要原因是因为公有云的弹性都是要预留资源的,这部分成本都会摊到消费者头上。 - -很多企业的业务资源使用都是相对固定,半年一年作一次扩容等,托管或者自建肯定会更便宜,促销活动什么的一年也就几次,在促销时使用公有云即可,这样成本可以大幅度降低。 - -## 云计算会走向开源开放 - -封闭的云服务对于企业来说是灾难,最简单的一个场景是应对云厂商的涨价行为,如果强绑定就意味着失去了议价权,近期某云厂商云开发就提价十倍,有些小企业的利润直接就被云服务吃光了。 - -第二个原因是云厂商的云产品如果发展的不好是有可能被下架的,如果企业不幸使用了这类产品,下架时又需要付出巨大迁移成本,有些与代码耦合的甚至需要重写代码。 - -开源自然是开放的最好实现方式,不仅对上面几种场景有比较好的应对措施,关键还可以自由按照自己的需求进行定制。 - -所以未来开源与云是左右腿,像 vercel supabase [sealos](https://github.com/labring/sealos) 这样的产品是云计算的大势所趋。 - -## 基于内核架构的云计算会变得更简单 - -复杂的东西无法普及,复杂的软件要么走向腐烂和消亡,要么重构变得简单,云计算也是如此,你会发现 centos ubuntu 这样的 linux 发行版普及了,但是现在的一些公有云能力很难到处运行和做到普及,即便是开源了,像 openstack 一直未能普及,原因很简单,需要几十个人的团队才能在生产环境玩起来的话绝大多数企业都会放弃。 - -什么叫“内聚”,就是功能不是以牺牲复杂度来换取的,像 linux 的 core 很内聚,驱动即使扩展了一万个系统复杂度也没增加,虽然代码在一直增加。所以软件设计时的抽象能力就变得极重要,基于云内核架构设计的云操作系统也是高“内聚”的,通过扩展应用来扩展能力,而各应用之间是低耦合的。 - -## 内核架构云操作系统爆发时机 - -> 基于开源技术的云服务在侵蚀昂贵且强绑定的公有云的服务 - -现在可以发现公有云云原生领域提供的服务商业化做的好的几乎都是开源强相关的, 如基于 kubernetes 的云服务,基于 prometheus grafana 的可观测服务等。 - -用户越来越聪明了,便宜还是贵按按计算器就能算出来,而且绑定意味着认人鱼肉,技术选型明显往开源技术倾斜。 - -> 云原生侵蚀传统 IaaS 服务 - -基于虚拟机的业务增长速度已经远远赶不上云原生生态的发展速度了,基于 kubernetes 的云原生生态每年几倍甚至有些产品每年几十倍的增长,大量企业在从虚拟机架构往云原生架构迁移。 - -前几年市场被教育的很好,越来越多企业知道云原生降本增效不是一点点,该填的坑也被填的差不多了,开始考虑从观望状态变成实践了。 - -> 市场需要一款云操作系统进一步降低云原生门槛与成本 - -现状是企业在实践云原生的时候还是容易迷失,生态过于庞大复杂,上千款生态软件让很多企业无从下手,而且真要落地至少得有个专家能把云原生计算存储网络都玩的明白,所以这个生态依然还是缺乏好用的开箱即用的发行版。 - -其实这个发行版的要求还是很高的,要非常简单不多不少的去满足客户的需求,还不能给用户带来负担,这就必须得非常好的设计理念和实现机制。 - -## 如何实现这样一个云操作系统 - -如何去设计这样一个操作系统,首先一定需要有非常好的设计理念 - -1. 化整为零,这意味着如果你不装应用,这个系统就是空的,就是 nothing,就是 void*,就和你买了一台新电脑里面除了操作系统什么也没装一样。 -2. 自由组装,所有用户的需求都是通过具体应用实现的而这些应用都是按需求从应用市场中下载,不会硬塞给用户不需要的东西,未能得到满足的需求也是通过应用去扩展。云操作系统不会去追求各种应用风格的统一,就像 macOS 上的微信和飞书不会有统一的风格和账户系统一样,只有这样各应用才能在自己的场景尽情发挥出最大优势。 - -![image](https://user-images.githubusercontent.com/8912557/176412722-87da5eaa-2a27-424f-b429-e2cab7c00eb2.png) - -实现层面,core 是非常内聚的意味它向下仅提供云内核生命周期管理,如安装/伸缩/升级/清理,向上做好应用的打包与管理即可。 - -应用市场方面很重要,一定要有好的标准,这涉及到应用的提供者与消费者之间的协作,OCI registry 仓库就是个非常好的已有事实标准,兼容它是最好的选择。 - -User interface 一定要简单极致,这是用户直接使用你东西的地方,API > CLI > GUI, Desktop 是产品化的终极形态,真的做到用云像用 PC 操作系统一样简单。 - -剩下一切都在于扩展应用的宽度和深度: -1. 广度,常用分布式软件如 mysql 集群,redis 集群,消息队列等逐步覆盖,不断扩展常用分布式应用数量 -2. 深度,基本安装->高可用->可监控->自运维->高性能/安全性->产品化,几个阶段衡量一个分布式应用成熟度 - -那 [sealos](https://github.com/labring/sealos) 就是使用这样的思维去设计的,[laf](https://github.com/labring/laf) 就是 sealos 上的第一款杀手级应用。 - -## 总结 - -未来的云会更便宜 更开放 更简单,最终会有一款优秀的发行版本实现云原生的普及,而 [sealos](https://github.com/labring/sealos) 诞生之日起就朝着这个目标不断进步~ - -相信未来云计算属于所有算力的提供者,云的价值也会属于所有云计算的参与者,不再受任何厂商绑定之苦,更便宜的享受云计算带来的便利。开源开放带给大家简单/便宜的云计算! - -> 作者:fanux.方海涛.中弈 sealos 作者, CNCF sealer 项目发起人。曾就职阿里云,现任环界云计算 CEO, 环界获得陆奇博士奇绩创坛种子轮投资 - diff --git a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/envoy-resource.png b/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/envoy-resource.png deleted file mode 100644 index c3db14f508d..00000000000 Binary files a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/envoy-resource.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-controller.png b/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-controller.png deleted file mode 100644 index c4835a35efb..00000000000 Binary files a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-controller.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-gateway.png b/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-gateway.png deleted file mode 100644 index e607ff6143b..00000000000 Binary files a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/higress-gateway.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/nginx-resource.png b/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/nginx-resource.png deleted file mode 100644 index aa36990d2d9..00000000000 Binary files a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/images/nginx-resource.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/index.md b/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/index.md deleted file mode 100644 index 55d582f63ca..00000000000 --- a/docs/blog/zh-Hans/2023/how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice/index.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -slug: how-sealos-cloud-mastered-multi-tenancy-with-the-right-gateway-choice -title: Sealos 网关选型血泪史:云原生网关哪家强 -description: 探索 Sealos Cloud 如何在面对数十万条 Ingress 规则和严格的多租户要求中优化其网关选择。从 Nginx Ingress 的局限性到最终选择 Higress,这篇文章深入分析了各种网关选项的性能、稳定性和安全性,为公网环境中支持多租户场景的网关选型提供了宝贵的洞见。 -authors: [fanux] -tags: [Kubernetes, Sealos, 网关] -keywords: [云操作系统, Sealos, K8s, 云原生, 网关, Envoy, Ingress] -image: images/feature.jpg -date: 2024-01-20T10:00 ---- - -[Sealos](https://sealos.run) 公有云几乎打爆了市面上所有主流的开源网关,本文可以给大家很好的避坑,在网关选型方面做一些参考。 - - - -## Sealos Cloud 的复杂场景 - -[Sealos 公有云](https://cloud.sealos.io)上线以来,用户呈爆发式增长,目前总共注册用户 8.7w,每个用户都去创建应用,每个应用都需要有自己的访问入口,就导致整个集群路由条目非常巨大,需要有支撑数十万条 ingress 的能力。 - -另外,在公网提供共享集群的服务,对多租户要求极为苛刻,用户之间的路由必须不能相互影响,需要非常好的隔离性,以及流量控制能力。 - -公有云的受攻击面是很大的,黑客会攻击云上跑的用户应用,也会直接攻击平台的出口网络,安全性上也有非常大的挑战。 - -对控制器的性能和稳定要求都比较高,很多控制器路由条目一多时消耗资源会非常大,甚至 OOM 导致网关奔溃。 - -## 排除 Nginx Ingress - -我们最早用的就是 Nginx Ingress, 最后发现有几个核心问题无法解决: - -* reload 问题,每次有 ingress 变更会导致断连一小会,而一个集群用户一多的时候,ingress 的创建变更会是个频繁事件,就会导致网络经常不稳定。 -* 长链接不稳定,也是因为变更,在用的长链接会经常断。 -* 性能不行,生效时间慢,消耗资源多。 - -所以几乎排除掉了很多底层用 Nginx 实现的网关。我们实测下来基于 Envoy 实现的网关性能彪悍太多,几乎控制面和数据面都不怎么消耗性能。 - -这是 Envoy 的: - -![](images/envoy-resource.png) - -这是 Nginx 的: - -![](images/nginx-resource.png) - -差距非常之大,所以我们就可以排除掉 Nginx 系列选项了。彻底拥抱 Envoy。 - -## 关于 APISIX - -APISIX 本身是个优秀项目,解决了 Nginx reload 的一些问题,所以我们 [Laf](https://laf.run) 早期也用了 APISIX,但是很不幸 APISIX 的 Ingress Controller 并不是很稳定,控制面奔溃给造成了我们好几次大的故障,还出现过控制器 OOM 等问题,我们本来真的很想用,但是最终还是因为故障问题被强制劝退,当然 APISIX 社区也在一直跟进这些问题,希望能越做越好。 - -总结一下就是: APISIX 本身稳定性很好,但是控制器需要优化的东西还很多,稳定性也有待提高。社区支持力度也很大,无奈我们线上问题火烧眉毛没法按照社区的节奏慢慢迭代,只能先切成别的网关了。 - -## Cilium Gateway - -[Sealos 的 CNI](https://sealos.run/docs/self-hosting/lifecycle-management/quick-start/deploy-kubernetes#%E5%AE%89%E8%A3%85-k8s-%E9%9B%86%E7%BE%A4-1) 很早就切换成 Cilium 了,确实很强,所以我们想着网关也统一用 Cilium 得了,但是现实很骨感。 - -Cilium Gateway 只支持 LB 模式,这样就强依赖云厂商的 LB,而我们也有一些私有化的场景,所以不希望耦合,稳定性方面也遇到了路由非常多的时候 Ingress 生效特别慢的问题,需要分钟级生效,这样用户的体验就很差了,我们能接受的是 5s 内路由生效。所以结论就是只能再等等。 - -## Envoy Gateway - -K8s 标准的发展来看,会逐渐从 Ingress 迁移到 Gateway 的标准,而我们底层又更倾向使用 Envoy,那 Envoy Gateway 的实现似乎是一个很好的选择,所以我们调研了 Envoy Gateway, 但是这个项目还是太过于早期,遇到了一些不稳定的 bug,比如会 OOM,pathpolicy 不生效,有些特性在 merge gateway 模式下不生效等问题,在持续解决中,我们也在不断帮助上游社区提改进意见和贡献,希望未来可以能达到生产可用的状态。 - -## 逼格很高但不那么实用的 Gateway 标准 - -Gateway 的处境很尬感,我的感觉是设计者并没有真的实践过多租户场景,当多租户共享一个集群时,就要明确区分管理者和使用者的权限问题,Gateway 设计之初就没完全考虑清楚,举个例子: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: eg -spec: - gatewayClassName: eg - listeners: - - name: http - port: 80 - protocol: HTTP - # hostname: "*.example.com" - - name: https - port: 443 - protocol: HTTPS - # hostname: "*.example.com" - tls: - mode: Terminate - certificateRefs: - - kind: Secret - name: example-com -``` -这里监听端口这类的配置应该是给集群管理员而不是普通用户,而 TLS 证书的配置属于某个应用,管理员可以有权限配置,主要还是每个用户去配置自己的,所以这里面权限就没有分开。 那就只能让用户也有权限配置 Gateway,所以这里就又需要在控制器里实现很多的权限控制的细节问题,如端口号白名单,冲突检测等。 - -个人觉得更优雅的设计是把其中租户级别的字段下沉到 HTTPRoute 中实现,或者一个单独的 CRD,这样用户态和超级管理员就可以分开的更清楚。 现有的方式也能做,就是有点混杂。 - -## 最终 Higress 胜出 - -除了以上重点的项目我们还测试了很多其他项目,我这里就不一一列举了。 Sealos 最终选了 Higress。 - -我们目前选择网关的逻辑很简单,主要就是在满足我们功能的前提下足够稳定,最终选择 Higress 几乎是排除法得出来的。 - -稳定性是排在第一位的,在我们的场景里面能够达到生产可用的目前只有 Higress,不过实践过程中也出现过一些问题,好在 Higress 社区的支持力度很大,很快速的解决了,主要有几个: - -1. ingress 生效速度慢,路由条目多时 2min 多新建路由才能生效,社区最后优化到了 3s 左右,这已经到极致了,也没有再优化的必要了,因为已经比容器 Ready 时间还短了, Higress 使用了一种增量加载配置的机制,让海量路由条目时也能有夸张的性能。 -2. 控制器 OOM,在无动态加载时资源消耗比较大,出现过 OOM 的情况,目前三高问题都解决掉了。 -3. 超时问题,有一个进一步优化加载延时的参数配置 onDemandRDS 在我们一个主集群会偶发请求超时,目前是把该配置关闭了,还在进一步查看原因,而在其它集群中未发现这个问题。 - -安全性方面,我们很多时候的故障问题都是性能问题造成的,流量过大,打爆网关比较常见,所以网关的性能变得至关重要,实测下来 Envoy 要彪悍很多,控制器写的好不好也生死攸关,这个方面 Higress 表现出众: - -![](images/higress-controller.png) - -![](images/higress-gateway.png) - -在我们已经海量路由,超高并发的情况下,需要的资源少的可怜。 - -Higress 还兼容 Nginx Ingress 语法,主要是一些 annotations,我们之前的代码都是用的 Ingress,所以几乎没有任何迁移成本,直接几分钟的升级就可以搞定。 - -同样为了促进社区更好的发展我们也给 Higress 一些意见: - -* 能对 Gateway 的标准有更好的支持,目前虽然已经支持了 v1 版本,但还没有完全兼容 ingress 上的能力。 -* 能开放出一些大杀器的功能,比如安全和熔断方面的能力。让开源和商业结合的更紧密一些,我们倒是不排斥付费,但是随着平台发展,需要更强的一些功能。 -* 周边功能建议更多通过插件机制扩展,让核心功能更内聚一些,简单可依赖。 - -## 总结 - -网关对于云和应用而言是个非常非常核心的组件,随着我们规模的不断扩大也会出现很多新的挑战,我们希望能和上下游社区建立紧密的合作,让开源网关能得到更好的发展,让更多开发者受益。 - -以上列举的很多网关都很优秀,Sealos 没用不代表项目不厉害,只是我们的场景苛刻且奇葩,真的在公网环境能支持多租户的网关并不多,所以各位看官还是要从自己的场景出发,我们的选型仅作参考,同样 Sealos 本身也会以一个开放心态来继续跟进其他网关的发展。 - -最后非常感谢 Higress 开源社区的大力支持,感谢阿里云云原生团队开源了这么优秀的项目,造福广大社区用户。 diff --git a/docs/blog/zh-Hans/2023/k8s-multi-tenancy.md b/docs/blog/zh-Hans/2023/k8s-multi-tenancy.md deleted file mode 100644 index c55f3bb9151..00000000000 --- a/docs/blog/zh-Hans/2023/k8s-multi-tenancy.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -slug: k8s-multi-tenancy -title: K8s 多租户方案的挑战与价值 -description: 本文深入探讨了 K8s 多租户的概念、其在现代企业中的应用价值,以及实现这一机制所面临的技术挑战和解决方案。 -authors: [fanux] -tags: [Kubernetes, Sealos, 多租户] -keywords: [云操作系统, Sealos, K8s, 云原生, 多租户, 隔离, 命名空间] -image: https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-17-36-fBsk9p.jpg -date: 2023-11-29T10:00 ---- - -在当今企业环境中,随着业务的快速增长和多样化,服务器和云资源的管理会越来越让人头疼。K8s 虽然很强大,但在处理多个部门或团队的业务部署需求时,如果缺乏有效的多租户支持,在效率和资源管理方面都会不尽如人意。 - -**本文将深入探讨 K8s 多租户的概念、其在现代企业中的应用价值,以及实现这一机制所面临的技术挑战和解决方案。** - - - -## K8s 多租户的价值 - -“多租户”是一种软件架构的设计方式,允许多个用户(租户)共享相同的系统或程序组件,同时保持各自数据的隔离性和安全性。在 K8s 环境中,实现有效的多租户机制意味着能够在同一 K8s 集群中运行多个独立的租户工作负载,而无需担心资源冲突、数据泄露或安全问题。 - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-rLPyaY.jpg) - -### 没有多租户支持的挑战 - -当企业购买很多服务器并安装 K8s 后,企业内部多个部门 (例如 20 个) 可能都需要在同一集群上部署各自的业务应用。在没有多租户机制的情况下,这种集群使用方式有很多弊端: - -1. **低效率:**每个部门不能自主使用集群,必须通过集群管理员进行部署和管理。不仅减慢了部署进程,还可能造成排队等待的情况,大大降低工作效率。 - -2. **资源利用不充分:**业务应用不能混合部署,需要对服务器资源进行划分,最终可能会导致资源无法充分利用,造成浪费。 - -3. **业务和资源管理混乱:**在一个没有租户隔离的集群中,各部门的业务相互干扰,难以管理。随着时间的推移,集群的管理和运维变得越来越复杂。 - -4. **规模扩展受限:**在一个单一租户的环境下,集群难以支持多样化的业务需求,限制了企业的扩展能力。 - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-yua4G3.png) - -### 多租户架构的优势 - -一旦有了多租户能力,企业就可以真正意义上构建自己的云环境,实现资源的最大化利用和高效管理: - -1. **资源管理有序:**通过账户系统,每个部门可以自主管理其资源使用,无需担心资源分配和使用上的混乱。 - -2. **效率大幅提升:**各部门可以独立进行业务部署和更新,无需麻烦集群管理员,极大提高了操作效率和业务的灵活性。 - -3. **资源扩展灵活:**集群管理员只需关注整个集群的资源状况,而不是单个业务应用,资源按需分配和扩展会更加灵活和高效。 - -4. **业务隔离保障稳定性:**不同部门的业务应用在集群中彼此隔离,避免了相互干扰,保障了业务的稳定运行。 - -## K8s 多租户的挑战 - -在 K8s 环境中实现多租户架构难度非常大,不是简单使用命名空间的能力就能实现的,还涉及到非常多的技术挑战。 - -### 挑战 1:防止越权 - -在 K8s 多租户环境中,限制每个用户的权限是关键。当多个用户共享一个集群时,一个权限过高的用户可能会对整个集群构成致命威胁。例如,禁止用户访问服务器节点或执行节点级别的操作,如使用 `kubectl get node` 命令。此外,需要限制其他高风险操作,如启用容器特权模式、共享主机文件系统、端口和网络等。 - -为了解决这些问题,Sealos 在其底层架构中采用了多种隔离手段。例如,使用 OpenEBS 进行存储的块级别隔离,Firecracker 以及 Cloud Hypervisor 用于计算运行时的隔离,以及通过 Cilium 实现网络隔离。这些措施确保即使在共享环境中,每个租户的操作也不会影响到其他租户。 - -### 挑战 2:用户的概念、授权与命名空间绑定 - -K8s 本身不具备原生的用户管理系统。因此,需要通过扩展功能来构建用户概念,与第三方用户系统对接,为每个用户生成独立的 kubeconfig 认证文件或 token。此外,需要建立用户与命名空间 (namespace) 之间的多对多关系,并为用户分配适当的权限。 - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-Dfn5xa.png) - -Sealos 的设计允许管理员将用户加入特定的命名空间,并对其角色进行管理,从而有效地控制权限。这样管理员就可以细粒度地管理用户权限,确保每个用户只能访问和修改他们被授权的资源。 - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-wknQxI.png) - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-34-RQFrTB.png) - -### 挑战 3:计量与配额管理 - -在多租户环境中,合理地分配和监控资源使用是另一个重大挑战。需要明确每个租户使用了多少 CPU、内存、磁盘和网络资源,并在资源使用超出配额时进行适当的处理。网络计量尤其复杂,需要区分内外网流量,而且要追踪到达特定容器的流量,并确定这些容器属于哪个租户。 - -Sealos 采用 eBPF 技术来监控网络流量,并通过控制器将流量数据与租户信息相关联,存储到数据库中。这样可以与计量计费系统对接,实现对资源使用的准确计费。对于计算和存储资源的监控,Sealos 同样采用了控制器来收集和管理这些信息。 - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-36-HsycaI.png) - -## Sealos 多租户的挑战 - -如果说上面的这些问题很难解决,那么 **Sealos 的场景是在上述难度上乘以了 10 倍**,因为 Sealos 选择了在公网这个不可信的环境中解决多租户问题,意味着给任意的开发者公开注册,然后一起共享一个 K8s 集群。 - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting6@main/uPic/2023-11-29-10-54-kbCMsN.png) - -公网环境的不可信性和开放性使得实现多租户变得尤为复杂。在这种环境下,任何开发者都可以注册并共享同一个 K8s 集群,这就带来了巨大的安全和稳定性挑战。但是,如果能够成功实现,其好处也是显而易见的: - -1. **成本效益**:用户无需单独搭建和维护完整的集群,显著降低了云服务的使用成本。 -2. **资源优化**:允许每个容器运行在更小的规模上,充分利用平台的弹性和资源。 -3. **强隔离性**:在公网环境中实现良好的多租户隔离,可以确保更高的安全性和稳定性。 - -前途的光明的,但挑战也是巨大的。 - -### 挑战 1:网关限制 - -Sealos 目前拥有数万注册用户,流量很大,我们几乎已经打爆了市面上很多主流的开源网关。在这种情况下,一个用户的更新可能导致所有其他用户受到影响,因为 Nginx 需要重新加载配置,这显然是不能接受的。 - -还有某知名网关 (不便透露),控制器 CPU 很容易就被打爆。 - -还有某知名网关 (同样不便透露),数量一多配置生效需要超过两分钟。不过我们已经和上游社区进行了反馈,应该很快会有改进。 - -### 挑战 2:运行时隔离问题 - -Sealos 需要实现强隔离以保证多租户环境的安全。然而,市面上的主流运行时环境并不能满足 Sealos 的隔离需求。例如,Firecracker 无法提供对 GPU 的良好支持,这对于需要高性能计算的应用来说是一个比较严重的限制。 - -### 挑战 3:存储隔离问题 - -Sealos 需要确保不同租户的数据彼此隔离,防止数据泄露或被其他租户错误访问。这就需要实现块级别的存储隔离,挑战也很大。 - -### 挑战 4:网络计量和争用管理 - -最后,网络资源的计量和管理也是多租户环境中的关键问题。Sealos 需要准确地计量每个用户的网络使用情况,并且在资源有限的情况下合理地分配网络资源。当网络资源产生争用时,需要有机制来公平地解决这些争用,确保所有用户都能公平合理地使用网络资源。 - -## 总结 - -多租户成熟了才能算作是一朵真正的云,才能把云的威力发挥到九成以上。面对公网这一极其复杂和不可预测的环境,Sealos 不仅实现了多租户的隔离和安全,还在保障高效运行的同时,降低了成本。且底层使用了非常多优雅的技术方案,彻底解决企业所有开发者共享一朵云的需求。 diff --git a/docs/blog/zh-Hans/2023/sealos-release.md b/docs/blog/zh-Hans/2023/sealos-release.md deleted file mode 100644 index 948e4514a9a..00000000000 --- a/docs/blog/zh-Hans/2023/sealos-release.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -slug: sealos-release -title: Sealos - 云操作系统的未来 -description: 深入探索 Sealos 的发展历程,从一个简单的 K8s 安装工具到一个全面的云操作系统项目。了解创始人的故事,Sealos 的演变,以及它如何简化企业和个人的云计算操作。 -authors: [fanux] -tags: [Kubernetes, Sealos] -keywords: [云操作系统, Sealos, K8s, 云原生] -image: https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting5@main/uPic/2023-08-31-09-52-gLmSek.jpg -date: 2023-06-13T10:00 ---- - -## 这是个宏伟的计划 - -这是一个宏伟的计划,漫长且有趣。 - -在今天这个快速发展的云计算领域,Sealos 不仅是一个项目,它是对未来云操作系统概念的重新定义和实践。从一个简单的 K8s 安装工具开始,Sealos 的发展已迈入了全新的领域,目标是构建一个完整、高效且易于管理的[云操作系统](https://sealos.run)。 - -2018 年的某个夜晚,夜深人静,我挥舞键盘,敲下了 Sealos 的第一行代码。当时仓库命名为 “kubeinit”,后来觉得格局太小,我不可能只做一个安装 K8s 的工具。安装只是更大计划的一部分,于是更名为 [Sealos](https://github.com/labring/sealos/ "Sealos"),一个宏大的[云操作系统](https://sealos.run)计划就此诞生! - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting5@main/uPic/2023-08-31-09-52-gLmSek.jpg) - - - -Sealos 的第一个版本写完后,我就把它发布到了阿里云市场出售,**每份售价 15 元**。我没想到真的会有人买,当第一笔 15 元进账时,我异常兴奋,仿佛一个商业帝国就在眼前。但是,结果是我花了一整天时间为这位客户提供售后服务。。。**电影院里还在帮用户解决问题**。 - -先来一波回忆杀: - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-17-59-VHeU8B.jpg) - -随后销量暴增,很快我就换了新手机 iPhone 8,但是问题也同时增加,以至于我根本无法及时提供所有的售后服务。于是我决定重写 Sealos,发布了基于 Ansible 的 v2 版本。最终还是觉得没有做到极致,因为用户还是遇到太多依赖问题无法解决。直到读完 kube-proxy 的源码,我发现有一种方案可以把负载均衡变得更简单,**干掉所有依赖**。于是我编写了 Sealos 的 v3 版本,在安装方面做到了极致。 - -### 为何一开始专注于安装 ? - -因为安装是入口,绝大多数人在学习云原生技术时都无法避开这个问题。**安装的流量入口足够大,无疑是一个绝佳的切入点**。一旦用户习惯使用 Sealos 进行安装,就会逐步探索 Sealos 的其他功能。 - -### 在阿里的工作 - -**在阿里工作期间,我开发了 Sealer**。这里最重要的一点就是,让安装足够灵活。以前用户只能使用我创建的安装包,而集群镜像的创新可以让用户自由定义安装包,也可以自由组合任何安装包。这里有个让我感到自豪的想法 : **把整个集群视为一个整体,把 K8s 看作一个操作系统,那么在这个[云操作系统](https://sealos.run)中,“云版 Docker 镜像”会是什么样子?**这无疑是一个伟大的想法,极具抽象度和灵活性。 - -```dockerfile -FROM kubernetes:v1.25.0 -COPY mysql . -CMD helm install mysql . -``` - -这种构想让**[云操作系统](https://sealos.run)也像单机操作系统一样有了“镜像”**,伟大的构想又完成了一个环节。 - -### 创业第一年 - -那么,Sealos [云操作系统](https://sealos.run)最终会演变成什么样子呢?这是一个难以言状的问题,我只有一个朦胧的设想,隐隐若现。直到创业过程中连续迭代了三个版本,才有了今天的形态——**一切皆应用!** - -理解这一点其实很简单,只需要把单机操作系统上安装的单机应用替换成各种分布式应用即可。整个数据中心,**你看到的不再是一台台孤立的服务器,而是一个整体,变成了一台虚拟的超级计算机。** - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-17-59-ffDeDv.jpg) - -这样简洁、清爽且臻至完美的[云操作系统](https://sealos.run),相信你在第一眼见到它的时候,便会喜欢上它! - -这就是我五年的呕心沥血之作 —— Sealos!献给大家~ - -## 云可以如此干净 - -Sealos 保持了极简的设计,没有任何多余的按钮。实现简洁与强大并行的功能,有时候难如登天,但我们仍在产品设计上投入了大量的心血。无论何人,使用 Sealos 都将沉醉在我们为之打造的舒适体验中。 - -在 B 端软件的世界里,付款者与使用者往往并非同一人,导致产品体验时常被忽略,最关键的还是要说服决策者。而 Sealos 不一样,我们坚信产品体验高于一切,如果我们在产品上花费大量精力最终导致失败,那也死而无憾。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-17-59-v1fPyx.jpg) - -这种黑白灰的设计风格会让你使用产品时**感觉像在喝白开水,而不是在喝饮料,更不是在喝洗脚水** (某些产品使用起来想死的心都有)。开发者已经够痛苦了,我希望你们在使用 Sealos 时心情美好。 - -Sealos 能一针见血地戳中应用的痛点,比如这个应用管理器 App Launchpad,30 秒就可以让你上线自己的应用。这里涉及到大量细节,比如自动配置公网域名,自动解决 HTTPS 证书问题等。 - -## 云可以如此便宜? - -我在 Sealos 上运行了 10 多个应用,包括三个数据库,还有博客,低代码平台,测试平台等,每天只花 4 块钱 : - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-17-59-X4FAjB.jpg) - -为什么能这么便宜? - -- 只需要为运行的容器付费,无需虚拟机,也无需创建整个 K8s 集群,打开直接用。 -- 自动伸缩,夜间用户量少时副本缩小到 1。 -- 我们可以充分利用公有云的弹性,编写大量自动化代码,夜间释放计算资源,降低成本。 - -这对于企业来说,可以减少大量的资源使用成本。我们自己就在 10 台服务器上运行了 7000 多个应用,这意味着什么?企业部署一套 Sealos 集群后,只要服务器资源利用率低于 70% 就可以不断向集群中添加应用,直到填满为止。 - -你可能会问,**为什么不能直接使用 Kubernetes?** 原因很简单,对于诸如讯飞这样的企业,应用分散在各个部门,这时多租户、隔离与协作会变成刚需,直接使用 K8s 会把集群搞乱,最要命的可能是一个部门或者用户不注意搞了个安全问题会让整个集群崩溃,而 Sealos 完美解决了这个问题! - -Sealos 可以帮助 80% 的企业降低 80% 的资源使用成本。 - -## 云可以如此自由 - -与其他管理平台或 PaaS 平台不同,Sealos 的核心设计理念是“一切皆应用”。不同的开发者,不同的角色使用不同的应用,这让每个用户在使用时都没有心智负担。就像安卓生态中有几十上百万个应用,你只关心自己使用的那几款应用,不用关心其他应用在做什么。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-17-59-OsRpZy.jpg) - -这样的设计有两个主要优势: - -### 懂不懂 K8s 都能愉快地使用 Sealos - -许多基于 K8s 的 PaaS 平台或发行版要么暴露大量 K8s 原生概念,要么屏蔽这些概念。这两种做法都不理想。 - -暴露大量原生概念对小白和新手不友好,屏蔽 K8s 则失去了灵活性和兼容性,对 K8s 老司机也非常不友好。 - -Sealos 采取了不同的做法。在这个平台上,不同的人可以使用不同的应用。比如你是开发者想写 CRUD,你可以直接使用 Laf 这个函数应用。如果你是 DBA,你可以直接使用数据库应用。在这种情况下,你完全不需要关心 Kubernetes,这些概念会被完全屏蔽。 - -如果用户是云原生专家,他们可以在 Sealos 上安装 Lens 和各种 K8s Dashboard,也可以打开终端敲各种原生命令。这就极大提高了灵活度。 - -### 自由组装 - -Sealos 非常关注应用间的相互配合。例如,你在 Sealos 上使用函数计算,默认数据库可能是 MongoDB,但如果你想用 PostgreSQL 怎么办?这时就可以在 Sealos 上安装一个 PostgreSQL 应用,然后通过服务发现直接在函数计算里面访问。因为在同一个集群内,可以直接通过内网 DNS 相互配合。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-18-00-HEtGtd.jpg) - -Sealos 精简而不简单,所有组件都可以卸载,这让云恰好满足你的需求——多一分则嫌多,少一分则嫌少。这也意味着无论是一台服务器还是上百台数据中心,都可以通过一条命令构建成一朵云。 - -## Sealos 到底能干什么 - -- 30 秒在 Sealos 上跑个 nginx demo,自动伸缩 -- 30 秒起各种数据库,业务系统内网直接连接数据库 -- 在 Sealos 上直接启动你写的各种编程语言业务 - -这三个能力是基础,其他的能力你可以慢慢探索,慢慢发现新大陆。 - -在运行自己业务上,我们针对这个场景做了很多细节优化,比如自动分配二级域名,自动横向伸缩,支持运行各种有状态服务等。 - -你会发现,借助 Sealos,**无论是部署一个拨测系统,还是运行一个低代码平台,都是信手拈来。您的博客也可以轻松托管在 Sealos 上,成本低廉。使用 Sealos 终端,运行任何兼容 K8s 的应用,自动化操作不再是难题。** - -更进一步发现:原来**有个 AI 在帮你自动做故障诊断,自动上线业务,甚至帮你写代码并自动测试上线**。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-18-01-t4W7LN.jpg) - -然后你会发现普通人也能用 Sealos: - -+ 你可以在 Sealos 上快速安装财务软件; -+ 你也可以在 Sealos 上快速安装知识库,给企业所有人写笔记; -+ 你还可以在 Sealos 上快速安装聊天软件供企业内部沟通协作。 - -到这里你会惊喜地发现:Sealos 竟然什么都能干,真的通用,而且还如此简单!最后你会有所领悟,**原来这就是[云操作系统](https://sealos.run)!** - -## 真的有人在用 Sealos 吗 ? - -当然有,Sealos 社区用户 10 万+,不乏各种大企业。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-16-18-01-ZOZ4Ud.jpg) - -上线两个月时间注册用户已经破万,云服务共计运行 **7000+ 应用**。 - -**只有一些小微应用适合 Sealos 吗 ?** - -当然不,Sealos 的客户中有**国健大数据**,在疫情期间支撑健康码的服务,一秒钟都不能挂的高并发业务。也支撑过超大规模的 GPU 集群,每天处理 80T 数据,整个集群 80PB 数据。**聚道云**上百个应用跑在 Sealos 平台上。 - -## 阶梯计划 - -Sealos 的宏图不止于此,我们的目标是进化为一款无所不在的[云操作系统](https://sealos.run),为人们提供如同使用个人电脑般简易的云服务体验。借助 Sealos,企业可轻松实现: - -- 迅疾如闪电,一分钟上线新业务 -- 一年可缩减半数成本 -- 简单如拨动开关,一键起一朵云 - -企业用云,一款 Sealos 就足矣。 - -未来,我们将继续秉持工匠精神,精心打磨 Sealos 中企业所需的常用应用,如数据库、消息队列、推理能力、各类编程语言执行环境等。 - -Sealos [云操作系统](https://sealos.run)中还会**内置一个 Copilot**,它像一位航海家的副手,它可自动进行云原生改造,帮助开发者轻松迈入云原生的大门,也可以像专家一样帮助诊断集群问题,安全漏洞,并给出专业操作建议。 - -## 总结 - -历经五载,Sealos 总算实现了我当初写下第一行代码时的愿景 —— [云操作系统](https://sealos.run)。 - -感谢第一个为我付了 15块的同学,您的信任与鼓舞犹如一笔巨资,赋予我前行的力量。 - -感谢社区的全体贡献者,尤其是始终与我同行的老崔同学,众行远。 - -感谢讯飞复杂业务场景的锤炼,让我对业务场景有更深入的理解。 - -感谢阿里云在我写 Sealer 时的支持与帮助,为 Sealos 集群镜像的底层能力铸造了坚实的基石。 - -感谢与我共同开启创业之旅的所有伙伴,是大家共同将一颗灵感的种子,种植成为现实的大树。 - -感谢奇绩创坛踹了我临门一脚,也**感谢陆奇博士**出乎意料的看好我们给了我们很大信心。 - -感谢李军院长康一教授,张海龙,高捷资本,金福资产给我们的帮助、指导和信任。 - -感谢每一位选择 Sealos 的用户,你们包容了整个迭代过程中的种种问题,与我们共同雕琢更完美的 Sealos。 - -预祝每一位选择了我们的决策者,此刻你们的决策绝对是明智的。现在,Sealos 已经跨越了全新的起点,未来我们一定不负众望,向大家交付一款完美的[云操作系统](https://sealos.run)。 - -欢迎大家来体验 Sealos [云操作系统](https://sealos.run)的魅力👉 [https://cloud.sealos.io/](https://cloud.sealos.io/) \ No newline at end of file diff --git a/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/feature.jpg b/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/feature.jpg deleted file mode 100644 index 47e90ce0728..00000000000 Binary files a/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/feature.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/sealos-database.png b/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/sealos-database.png deleted file mode 100644 index 96cc662cc03..00000000000 Binary files a/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/images/sealos-database.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/index.md b/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/index.md deleted file mode 100644 index 33401cf0619..00000000000 --- a/docs/blog/zh-Hans/2023/to-run-or-not-to-run-a-database-on-kubernetes/index.md +++ /dev/null @@ -1,144 +0,0 @@ ---- -slug: to-run-or-not-to-run-a-database-on-kubernetes -title: 在 K8s 上跑数据库,到底有没有意义? -description: 本文深入分析了 K8s 在数据库管理上的优势,包括稳定性、性能和运维效率。了解为什么将数据库集成到 K8s 是当今技术趋势的一部分,并探讨这一选择对企业和开发团队的意义。 -authors: [fanux] -tags: [Kubernetes, Sealos, 数据库] -keywords: [云操作系统, Sealos, K8s, 云原生, 数据库, 容器,] -image: ./images/feature.jpg -date: 2023-12-06T10:00 ---- - -昨天冯老板发了一篇文章探讨了[为什么将数据库放入 K8S 中不是一个明智的选择](https://mp.weixin.qq.com/s/4a8Qy4O80xqsnytC4l9lRg)。 - -如果是四年前有人质疑容器化数据库我觉得还可以 battle 一下,都 2023 年了还有人不能认清这个大势,我就有必要来谈谈我的看法了。 - - - -我从 K8s 0.9 版本时就开始做这件事,当时确实略早,CSI 都不成熟,到 1.0 才稍微稳定点,当时我在科大讯飞工作,负责的项目是建设和维护一整套系统,这套系统最终支撑了公司内部的 PaaS 服务。 - -我们构建了一个 30 台物理机的集群,别看这个集群很小,但是非常有技术含量,里面跑了近 3000 个应用,而且是各种类型的,包括但不限于微服务,数据库,消息队列,缓存等等。**这个集群被公司内部几百名开发人员同时使用,但是整个集群的运维工作只需不到半个人力就能完成,如果没有 K8s 这一切绝对不可能。** - -我们还在不影响上层应用的情况下,无感知地升级了 Linux 内核。这种无感知升级如果没有 K8s 的支持是无法想象的,光是和各个业务线沟通可能都需要半年。 - -我见过另外一个集群,跑了 400 个数据库而已,堆了 400 台服务器和 40 个人的运维团队,集群的整体利用率却不到 10%。整个集群无人敢动,只能一直堆人,人肉运维。这种情况虽然可以归咎于组织的不专业,但实际上,很多团队都面临着类似的挑战,无法有效地管理和优化他们的基础设施。 - -后来我去了阿里,所有的交付类场景数据库全部是跑在 K8s 上。迄今为止我们在容器里跑数据库五年有余,0 故障。 - -## 数据库 on K8s:专业能力的普及化 - -绝大多数做业务的公司对数据库的处理通常存在两个问题:要么是数据库管理水平一般,无法充分发挥数据库的潜能;要么是每年需要在数据库管理上花费大量成本。[数据库 on K8s](https://sealos.run/docs/guides/dbprovider/) 可以让这一切标准化,有了标准,人与人之间才可以协作,生产力改变生产关系,从而大幅提效,让绝大多数不具备专业能力的团队享受到专业能力,本质上分工更明确了,就像农业和畜牧业分离一样,各自专注于自己的领域,从而提高整体的效率和产出。 - -以 KubeBlocks 团队为例,我相信绝大多数公司在数据库层面的积累和专业能力都没有他们强。而且他们将这些实践经验转化为代码,写成了控制器,以极其简单的方式赋能给其他企业。K8s 让这一切成为可能。 - -你可能会问:为什么不用 Ansible?运维人员可能很推崇 Ansible,因为和他们手头上的工具很匹配,用起来很顺手。Ansible 的核心思想是帮助用户部署和执行运维操作,而 K8s 的控制器则是基于另一种思路:**机器能做的事就不应该由人来做**。通过 Operator,可以实现 **24 小时不间断地同步期望状态和实际状态**,而这是用 Ansible 很难实现的,你用 Ansible 实现是想写个定时任务嘛? - -这就像在操作系统诞生之前,程序员需要手动给纸带穿孔来运行程序。有人可能会说,用纸带也能运行程序,甚至可以把程序刻录在光盘上运行,为什么还需要操作系统呢? - -这其实是同样的道理:Ansible 对运维人员来说是一款好工具,但 K8s 的目标是消除低端运维工作 (即编写和执行 Ansible 脚本的工作)。通过 K8s,我们可以实现更高效、更自动化的数据库管理,从而让那些不具备专业数据库管理能力的团队也能享受到专业级的服务。 - -## 数据库 on K8s 的优势 - -大部分人对于在 K8s 上运行数据库的担忧无非就集中在这几个问题上: - -**稳定性不知道怎么样?** - -**出了问题我没法排查?** - -**性能是不是不够好?** - -### 复杂度 - -在 K8s 上运行数据库,复杂度主要分为两个方面: - -1. 建设这套系统的复杂度 -2. 使用上的复杂度 - -**第一:建设这套系统的复杂度** - -如果直接基于原生的 K8s (裸 K8s) 去构建数据库系统,成本会相对较高,而且对于新手来说,这样的操作并不友好,你需要自己建设 K8s 存储驱动、数据库控制器等多个组件,没有深厚的专业知识和实践经验是搞不定的。 - -这个时候发行版的优势就体现出来了,类似于 Linux 系统中,大多数人更倾向于使用 CentOS、Ubuntu 等发行版,而不是直接操作内核。我们也可以将 K8s 视为一种 “云内核”,如果你只是直接使用内核而不进行适当的定制和优化,可能会觉得它不够好用。因为内核本身只是提供了一个框架,很多功能和优化需要用户自己去实现。而 K8s 发行版则帮助用户解决了这一问题。例如,[Sealos 可以帮你一键](https://sealos.run/docs/self-hosting/lifecycle-management/quick-start/deploy-kubernetes)构建**包括高可用性集群、存储插件和数据库在内的完整系统**。这一切只需要简单的两条命令: - -```bash -$ sealos run labring/kubernetes:v1.27.7 labring/helm:v3.9.4 labring/cilium:v1.13.4 \ - --masters 192.168.64.2,192.168.64.22,192.168.64.20 \ - --nodes 192.168.64.21,192.168.64.19 -p [your-ssh-passwd] -$ sealos run labring/openebs:v3.9.0 labring/mysql:8.0 -``` - -然后就没有然后了,一个包含高可用集群、存储插件和数据库的系统就诞生了。虽然 Ansible 可以帮助你解决安装问题,但它**无法处理运行时的自愈、多租户等问题**,而 on K8s 可以让数据库 as a Service。 - -**第二:使用上的复杂度** - -通过云操作系统发行版和控制器,用户可以实现产品化的数据库服务,而不是靠脚本解决问题。 - -![](images/sealos-database.png) - -这个页面我相信没有人不会使用吧?即使是菜鸡如我,都有能力建设起一个具有 3 副本的 PostgreSQL 集群,并且包含备份、恢复和监控等功能。这种能力不仅可以赋予企业中的所有开发者,也**展示了 “云计算思维” 与 “脚本思维” 的根本区别**。**云计算让每个人都能够提供服务 (as a Service),而传统的脚本方法只是运维人员的一种便捷工具。** - -### 稳定性 - -我们团队在数据库领域谈不上专业,都能建立起相当稳定的数据库系统,更别说专门研究这个领域的顶尖专家了。这个事情使用者不用操心,扔给专业的人去做就可以了。 - -举个例子,[Sealos 公有云](https://cloud.sealos.io)目前运行了数千个应用,这些应用的数据库都是完全容器化的,由 KubeBlocks 团队提供支持。一旦数据库出现任何问题,我们只需将问题扔给他们即可。从成本角度来看,随便招聘一个 DBA 的成本都远高于我们支付 KubeBlocks 商业版的费用了,而且 Sealos 还是平台的建设方,对于使用数据库的最终用户来说就更不用关心了。从目前的运行情况来看,我们的稳定性已经远超许多非专业团队的运维水平。 - -而且基本上数据库的生命周期管理就那么多事,稳定性问题是会随着时间的推移被收敛的,这些问题不断在代码层面被解决掉,最终用户关心的越来越少。这一点类似于 Linux 系统的稳定性,随着技术的不断成熟和优化,其稳定性已经达到了非常高的水平。**一个良好的软件架构会不断提升和收敛其鲁棒性,并逐渐减少对人的依赖,比如使用 Oracle 的人喝茶时间一定比用开源 MySQL 的人喝茶时间多。** - -所以无论从现实情况还是理论分析来看,稳定性都不应该成为用户在 K8s 上运行数据库的障碍。**将数据库运行在 k8s 上,实际上是在利用几十名顶尖数据库专家的经验,他们将自己的知识和技能沉淀到代码中,以标准化的方式为用户服务。单靠脚本很难将这些经验沉淀得如此彻底和高效。**。单靠脚本很难将这些经验沉淀得如此彻底和高效。 - -### 性能 - -说数据库跑容器性能不好的大概率都是不会玩的,KubeBlocks 团队做过深入的测试与调优,并撰写了很详细的分析文章,很多人觉得真复杂,但是其实这个复杂的事又不需要用户去做。**这些复杂性已经被内嵌在控制器的代码中,对于最终用户来说,这一过程并不复杂**。而且,容器对数据库性能的影响几乎可以忽略不计,真正重要的是磁盘 IO 和网络带宽时延等因素。 - -OpenEBS 裸盘+数据库控制器的方案就可以有效解决性能问题。有了数据库控制器,就无需依赖于分布式存储。控制器能够保证数据库多副本的高性能和高可用性,无论是有状态服务还是无状态服务,对于用户来说都感觉不到差异。如果实例发生故障,控制器会自动进行调整。这才是一种极致的数据库使用体验。 - -[Sealos](https://sealos.run) 目前已经采用了这种解决方案,在保证高可用性的同时,又不牺牲性能。它可以直接对接裸盘,进行自动扩容、备份和恢复。如果节点发生故障,控制器会自动启动新节点,同步数据并将其加入集群。这些高级功能只能在云操作系统中实现,传统的脚本方法只能望尘莫及,而且后者通常还需要人工介入,比如半夜挂了就只能 on call 了。 - -所以**在 K8s 上运行数据库不仅没有性能问题,其稳定性甚至都超过了大多数运维人员的能力**。而且,这种方式已经做到了简单易用和自助操作,你要不要用? - -## 不脱离实际场景去否定和肯定 - -在讨论数据库是否应该容器化时,我们必须考虑不同的实际应用场景。 - -有些公司的数据库已经非常稳定的以非容器化的方式在运行了,也不差钱养着一群数据库专家,这样的情况当然没有动力把数据库搬到 K8s 上,搬出问题谁来背锅?例如,银行通常使用专门的 Oracle 一体机,只需支付订阅费用即可,这样的系统很难有迁移的动力。 - -然而,对于许多业务开发团队和组织来说,他们现在面临着一个新的选择:**以极低的成本获得高度专业的数据库能力,从而将核心团队的精力全部集中在业务开发上。** - -要达到这一效果,他们可以选择直接使用 RDS (关系数据库服务) 这样的数据库云服务,或者采用基于 K8s 的数据库解决方案。这种方法需要一个长时间运行的管理进程来替代人工角色,以赋予那些不懂数据库的团队相应的能力。这就是一个大的趋势,固定成本 (例如开发控制器的成本) 提升了,但是边际成本 (每个使用数据库的团队的成本) 会大幅降低。 - -当前有很多方案可以做到这一点,比如基于虚拟机或基于 Ansible,但毋庸置疑基于 K8s 的控制器在当前看来是最优解。即便是提供类似 RDS 这种能力的服务,底层使用 k8s 技术栈也是最优解。相比之下,虚拟机就不太行了,重,成本自然高,而且有更多的性能消耗。而像 Ansible 这类工具想要实现自助服务和多租户支持,更是异想天开。 - -## 总结 - -### K8s 的重要性 - -K8s 是个大杀器,像是无崖子一甲子的功力你能发挥几成,如果 K8s 不跑数据库,你大概只能发挥 1 成功力。用好 K8s 能够极大地增强数据库运维的效能。 - -### 技术进步带来的分工变革 - -随着技术的不断进步,数据库的管理者和使用者会逐渐分离,传统的人工操作正在逐步被自动化程序所取代。在这个过程中,标准化就成了有效协作的基石。目前没有看到比容器技术和 K8s 更强的事实标准诞生,因此,将数据库跑到 K8s 上是大势所趋。 - -### 实践案例和效益 - -目前已经有很多团队在成本、易用性、稳定性和性能等多个维度上成功实践了 K8s,取得了显著的成果,也尝到了这样做的甜头。由奢入俭难,一旦企业体验到了 K8s 带来的好处,很难再回到传统的运维方式。以 Sealos 为例,从 v2 使用 ansible,到 v3 完全转向 golang,[现在已经发展到 v4 和 v5](https://github.com/labring/sealos),这种技术的演进正是基于 “云计算” 和 “云操作系统” 的思维,而不是传统的 “运维脚本” 思维。脚本连个 API 都实现不了你我谈先进生产力?设计一个系统优先考虑的不一定是给人用的,而是给别的系统调用的,这样整个自动化才能起飞,这就是为什么 API > CLI > GUI 的原因。 - -### 运维角色的转变 - -目前还是有很多存量市场的 DBA 运维人员想保住自己的饭碗在唱衰这个方向,但是英明的决策者迟早会发现采用 K8s 可以大幅降低人力成本,提高效率和系统稳定性。**良禽择木而栖,希望很多运维同学能意识到你们在逐渐被取代是事实,当年我们做讯飞云的时候有近 40 人的运维团队,做完之后连运维这个组都没了**。在阿里云的时候我们团队也是 0 运维人员。 - -### K8s 的快速成熟和生态发展 - -K8s 在以极快的速度走向更成熟,生态在蓬勃发展,诞生了短期的乱象,让落地实践变得无所适从。但是不要担心,优秀的发行版一定会出现,发行版就在做 “熵减” 的事情,简化用户的使用体验,就像 Linux 内核到 Linux 发行版的演进一样,Sealos 就是其中一款基于 K8s 的云操作系统发行版。我最近一段时间回访了将近 200 名 Sealos 的付费用户,没有一个用户反馈上面的数据库不会用的,有反馈不稳定的,几个原因,磁盘满了,升级导致的问题等,这几个问题都被收敛掉了,最终趋近于 0,至少可以说是比用户自己搭建的稳定性高出好几个 9。 - -### 企业的选择 - -企业选不选这样的方案还是根据自己实际情况来判断,但是聪明的企业在尝试数据库 on K8s 之后会带来极大的好处,例如选择了 Sealos + KubeBlocks 的组合,就相当于拥有了: - -1. 一个拥有8年以上经验的专业 K8s 团队。 -2. 一个 P10 带了一帮 P8-9 的顶尖专业数据库团队。 -3. 一个极友好的产品体验,鲁棒性极高,性能极高的数据库系统。 - -连招聘一个专家的成本都不到。当然这种选择一定有阻力,阻力大部分来自于企业内部那些想保住自己饭碗其实可以不太需要的人。 - -我本可以对冯老板的论调逐条反击,但是边看文章边写还是太累了,碎碎念这些,希望看看到底有多少人能有更高级点的认知,希望能听到更多支持 OR 反对我们的声音,一起探索真理~ \ No newline at end of file diff --git "a/docs/blog/zh-Hans/2023/velero\347\232\204\344\275\277\347\224\250.md" "b/docs/blog/zh-Hans/2023/velero\347\232\204\344\275\277\347\224\250.md" deleted file mode 100644 index 9946ea53c64..00000000000 --- "a/docs/blog/zh-Hans/2023/velero\347\232\204\344\275\277\347\224\250.md" +++ /dev/null @@ -1,149 +0,0 @@ ---- -slug: Use velero to perform cluster backup on k8s -title: Use velero to perform cluster backup on k8s -authors: [xiao-jay] -tags: [kubernetes,sealos] ---- - -## velero的使用 - -文档:https://velero.io/ - -### 注意使用1.24的k8s - -1.25的k8s暂时会有coredns的pod起不来的问题,导致不能备份 - -``` -kubectl get pod -A -kube-system coredns-565d847f94-5b2wt 0/1 CrashLoopBackOff 2271 (4m1s ago) 8d -kube-system coredns-565d847f94-jtw9f 0/1 CrashLoopBackOff 2271 (88s ago) 8d -``` - - - -### 以aws作为存储举例 - -### 1、安装aws cli - -``` -apt install awscli -aws configure #登陆aws -``` - - - -创建bucket - -``` -BUCKET= -REGION= -aws s3api create-bucket \ - --bucket $BUCKET \ - --region $REGION \ - --create-bucket-configuration LocationConstraint=$REGION -``` - - - -### 2、安装velero - -``` -wget https://github.com/vmware-tanzu/velero/releases/download/v1.9.2/velero-v1.9.2-linux-amd64.tar.gz -tar -xvf velero-v1.9.2-linux-amd64.tar.gz -mv velero-v1.9.2-linux-amd64/velero /usr/local/bin/ -``` - - - -### 3、把aws作为备份存储 - -创建一个credentials-velero文件 - -``` -[default] -aws_access_key_id= -aws_secret_access_key= -``` - - - -``` -export BUCKET=xxxx -export REGION=xxxx -velero install \ - --provider aws \ - --plugins velero/velero-plugin-for-aws:v1.5.0 \ - --bucket $BUCKET \ - --backup-location-config region=$REGION \ - --snapshot-location-config region=$REGION \ - --secret-file ./credentials-velero -``` - -查看是否安装成功,availabe就是成功了 - -``` -root@yyj-master1:/home/ubuntu# kubectl get pod -n velero -NAME READY STATUS RESTARTS AGE -velero-7dd66cfb94-pbf47 1/1 Running 0 12m -root@yyj-k8s124-test-master1:/home/ubuntu# velero backup-location get -NAME PROVIDER BUCKET/PREFIX PHASE LAST VALIDATED ACCESS MODE DEFAULT -default aws sealos-test Available 2022-11-10 02:43:28 +0000 UTC ReadWrite true -``` - - - -### 4、Quick Start - -``` -git clone https://github.com/vmware-tanzu/velero.git -cd velero -``` - -#### Basic example (without PersistentVolumes) - -1. Start the sample nginx app: - - ```bash - kubectl apply -f examples/nginx-app/base.yaml - ``` - -2. Create a backup: - - ```bash - velero backup create nginx-backup --include-namespaces nginx-example - ``` - - 然后bucket里面就出现backup - - ![](https://tva1.sinaimg.cn/large/008vxvgGly1h7zu5ykpwxj30pv0ds0ts.jpg) - -3. Simulate a disaster: - - ```bash - kubectl delete namespaces nginx-example - ``` - - Wait for the namespace to be deleted. - -4. Restore your lost resources: - - ```bash - velero restore create --from-backup nginx-backup - ``` - - - -### 5、集群备份 - -每一个小时全部内容备份一次 - -```fallback -velero schedule create full-cluster-backup --schedule="0 * * * *" -``` - - - - - - - diff --git a/docs/blog/zh-Hans/2023/vm-sealos-create.md b/docs/blog/zh-Hans/2023/vm-sealos-create.md deleted file mode 100644 index 14352302d60..00000000000 --- a/docs/blog/zh-Hans/2023/vm-sealos-create.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -slug: using sh quick build a k8s cluster -title: using sh quick build a k8s cluster -authors: [xiao-jay] -tags: [kubernetes,sealos] ---- - -## 使用 multipass 虚拟出虚拟机然后基于 sealos 快速构建一主一从的 k8s 集群, - - -### 前置条件 -1、安装好multipass -如果需要虚拟机把ip暴露出来,需要配置一下网桥 -https://multipass.run/docs/networks-command - -2、~/.ssh必要创建好,使用本地ssh来作为虚拟机的ssh -``` -创建命令`ssh-keygen -t rsa` or `ssh-keygen -t rsa -C "xxxxx@xxx.com"` -``` - - -### 使用教程 - -``` -./start_vm.sh -./start_vm.sh name #指定集群名字 -``` - -脚本内容 - -``` -#!/usr/bin/env sh -# requirement -# ~/.ssh must have id_rsa.pub,id_rsa - -# Usage: -# - sh start_vm.sh -# - sh start_vm.sh Name - -NAME="laf-test" -# if set first param in command line -if [ -n "$1" ]; then - NAME="$1" -fi - -# check if multipass is installed -if ! command -v multipass; then - echo "multipass is not installed, please install it first" - exit 1 -fi - - -MASTER_NAME="$NAME""-master1" -NODE_NAME="$NAME""-node1" -# delete the vm if it already exists -if multipass list | grep -e "^$MASTER_NAME"; then - echo "existing vm $MASTER_NAME" - echo "Deleting the existing vm $MASTER_NAME" - multipass delete -p "$MASTER_NAME" -fi - -if multipass list | grep -e "^$NODE_NAME "; then - echo "existing vm $NODE_NAME" - echo "Deleting the existing vm $NODE_NAME" - multipass delete -p "$NODE_NAME" -fi - -echo "Creating VM..." -#multipass launch --name "$NAME" --cpus 2 --mem 4G --disk 100G -echo "\tmultipass launch --name $MASTER_NAME --cpus 2 --mem 4G --disk 100G " -multipass launch --name $MASTER_NAME --cpus 2 --mem 4G --disk 100G - -echo "\tmultipass launch --name $NODE_NAME --cpus 2 --mem 4G --disk 100G " -multipass launch --name $NODE_NAME --cpus 2 --mem 4G --disk 100G - -if [ $? -eq 0 ]; then - echo "vm is created" -else - echo "ERROR: failed to create vm, please retry" - exit 1 -fi - -# shellcheck disable=SC2139 -alias vm_root_exec="multipass exec $MASTER_NAME -- sudo -u root" -alias vm_node_exec="multipass exec $NODE_NAME -- sudo -u root" -echo "Installing sealos..." -set -x -vm_root_exec -s << EOF -echo "deb [trusted=yes] https://apt.fury.io/labring/ /" | tee /etc/apt/sources.list.d/labring.list -apt update -apt install sealos=4.1.3 -y -EOF -set +x - -set -x -vm_node_exec -s << EOF -echo "deb [trusted=yes] https://apt.fury.io/labring/ /" | tee /etc/apt/sources.list.d/labring.list -apt update -apt install sealos=4.1.3 -y -EOF -set +x - -set -x -cp -r ~/.ssh ~/vm_ssh - -vm_root_exec rm -rf /root/.ssh -vm_root_exec mkdir /root/.ssh -multipass transfer ~/vm_ssh/id_rsa $MASTER_NAME:/home/ubuntu/ -multipass transfer ~/vm_ssh/id_rsa.pub $MASTER_NAME:/home/ubuntu/ -vm_root_exec cp -r /home/ubuntu/id_rsa /root/.ssh/id_rsa -vm_root_exec chmod 600 /root/.ssh/id_rsa -vm_root_exec cp -r /home/ubuntu/id_rsa.pub /root/.ssh/id_rsa.pub - -vm_node_exec rm -rf /root/.ssh -vm_node_exec mkdir /root/.ssh -multipass transfer ~/vm_ssh/id_rsa.pub $NODE_NAME:/home/ubuntu -vm_node_exec cp -r /home/ubuntu/id_rsa.pub /root/.ssh/authorized_keys - -rm -rf ~/vm_ssh - -set +x - -master_ip=$(multipass info "$MASTER_NAME" | grep IPv4: | awk '{print $2}') -node_ip=$(multipass info "$NODE_NAME" | grep IPv4: | awk '{print $2}') - -echo master_ip: $master_ip -echo node_ip: $node_ip - -echo "Installing k8s..." -set -x -vm_root_exec sealos run labring/kubernetes:v1.24.0 labring/helm:v3.8.2 labring/calico:v3.22.1 --masters $master_ip --nodes $node_ip -#vm_root_exec kubectl taint node $NAME node-role.kubernetes.io/master- -#vm_root_exec kubectl taint node $NAME node-role.kubernetes.io/control-plane- -set +x -set +e - -echo "k8s cluster is ready." -``` - diff --git a/docs/blog/zh-Hans/2023/what-is-sealos.md b/docs/blog/zh-Hans/2023/what-is-sealos.md deleted file mode 100644 index 187e92491f7..00000000000 --- a/docs/blog/zh-Hans/2023/what-is-sealos.md +++ /dev/null @@ -1,349 +0,0 @@ ---- -slug: what-is-sealos -title: Sealos vs. 传统云服务:全方位对比分析 -description: 深入探讨 Sealos 的核心功能、技术特点、设计理念,以及它如何革新云操作系统领域。我们也将探索 Sealos 在不同使用场景下的应用,并与市场上其他云服务平台进行比较,以展现其独特的市场优势和潜力。 -authors: [fanux] -tags: [Kubernetes, Sealos] -keywords: [云操作系统, Sealos, K8s, 云原生, 云计算, 分布式, PaaS, Rancher, KubeSphere, 云服务] -image: https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-15-50-TKK1Ol.webp -date: 2023-07-10T10:00 ---- - -随着云计算的快速发展和广泛应用,企业和开发者越来越希望能够灵活、高效地管理和部署云资源。在这个背景下,Sealos 应运而生,它不仅是一个以 K8s 为内核的[云操作系统](https://sealos.run),更是一个创新的解决方案,旨在简化和优化云计算的使用体验。 - -本文将深入探讨 Sealos 的核心功能、技术特点、设计理念,以及它如何革新[云操作系统](https://sealos.run)领域。我们也将探索 Sealos 在不同使用场景下的应用,并与市场上其他云服务平台进行比较,以展现其独特的市场优势和潜力。 - - - -## Sealos 是什么? - -Sealos 在概念上类似于如 Windows 这样的操作系统,但有两个关键的不同点。首先,Sealos 不是在单个服务器上运行,它的核心理念是**将整个数据中心或跨多服务器的资源视为一个统一的整体**。这种方法突破了传统操作系统只在单一机器上运行的局限,将资源和应用管理扩展到了更大规模,能够**在整个数据中心范围内运行和管理应用**,从而大幅提升云资源的利用效率和运维能力。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-13-46-9Nel1a.png) - -与普通操作系统支持的 QQ、微信等日常应用不同,Sealos 专注于为开发者提供所需的分布式应用环境。在 Sealos 的世界里,**复杂的云计算任务变得像使用个人电脑一样简单直观**。无论是运行常见的 Web 服务如 Nginx,还是部署和管理各种编程语言编写的分布式应用,Sealos 都能一键完成,极大地减少了配置和管理的复杂性。它的设计哲学强调用户友好性和简洁性,致力于消除使用云服务时的技术壁垒,让每个用户都能轻松享受到云计算的强大能力。 - -## Sealos 解决的核心问题 - -Sealos 主要解决了以下几个核心问题: - -### 优化用云体验 - -#### 用户界面 - -用户界面 (User Interface) 的重要性不言而喻。传统的单机操作系统为我们提供了一个标准化的用户体验范例。然而,当今众多的云平台已经演变成庞大而复杂的系统,导致用户在产品中迷失方向。甚至催生了一些专门针对云服务的培训岗位,这在某种程度上反映了产品设计的失败。例如,很少有人需要参加苹果手机的使用培训,因为其产品设计已经足够优秀,非常易于理解和操作。 - -在产品设计理念上,我们首先需要认识到不同的用户角色关注点各异。在云服务的用户群体中,有开发者、数据库管理员 (DBA)、运维人员、熟悉 K8s (k8s) 的专家、技术新手和行业专家等。试图用一个产品满足所有这些角色的需求几乎是不可能的。例如,即使是同一个 CI/CD (持续集成/持续部署) 工具,也有人偏爱 Jenkins,而有人更喜欢 Drone。 - -许多 PaaS 平台以 CI/CD 为例,通常集成了特定工具如 Jenkins,这导致一旦该工具不再流行或有更优秀的替代品出现时,平台需重构核心功能。同时,这种设计无法满足不同用户的偏好。 - -单机操作系统的做法值得我们学习。操作系统本身并不过多干预,而是负责良好地管理应用程序。这样,用户就可以自由选择他们喜欢的应用,如办公软件钉钉或飞书,而这与 Windows 操作系统无关。这种方法赋予了用户极大的自由度。尽管许多 PaaS 平台也提供应用市场,但他们并没有将应用视为首要元素。相反,大多数平台将 K8s 视为核心,这不是说有什么大错,但**这种做法只能定位于云原生用户群体,并不能实现高度的抽象**。 - -Sealos 平台则完全遵循了操作系统的理念。它专注于用户所需的具体功能。例如,DBA 在创建数据库时无需关心 K8s 的具体细节;使用函数计算服务的用户不必关心其是否运行在容器中;而对于 K8s 技术专家,则可以通过 Lens 应用程序或命令行工具进行操作。Sealos 的这种设计理念,使得各类用户都能在其平台上找到合适的工具和服务,从而优化了整体的用户体验。 - -#### API > CLI > GUI - -许多人对产品的理解仅限于其 GUI,但实际上,一个没有 API 的云服务产品对企业而言几乎无用。企业为了提高效率,需要打通和对接各种系统,这时 API 的重要性就显现出来了。云服务的设计往往不仅仅是为了人类用户,更多的是为了其他程序或系统,以实现企业操作的高度自动化。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-14-36-qnQmDa.jpg) - -具体来说,Sealos 提供的 API 与 K8s 的 CRD (Custom Resource Definitions,自定义资源定义) 设计完全兼容。用户可以通过 Sealos 的 API,以与操作 K8s 环境相同的方式来管理和控制他们的云资源。为了安全性,Sealos 为每个租户分配了权限受限的 kubeconfig 认证文件。这些文件允许租户在保证安全的前提下,灵活地对接和管理不同的系统和资源。 - -这种设计不仅使得 Sealos 的云服务更加强大和灵活,而且为企业提供了一种高效、自动化的方式来管理他们的云基础设施。通过 API 的广泛应用,企业可以轻松地整合 Sealos 云服务到他们现有的工作流程中,从而提高运营效率和灵活性。 - -#### 快速、高效的操作体验 - -我们的目标是确保大多数操作能在 30 秒内完成,最长不超过 3 分钟。如果某项功能的操作时间超过这个标准,那一定是有问题的,需要重新评估和设计。 - -#### 面向所有用户 - -尽管 Sealos 主要面向开发者,但在功能设计过程中,我们同样关注非技术背景用户的体验。为此,我们特意邀请无技术背景的行政人员亲身体验我们的服务,以此来验证产品的易用性。如果他们能够顺畅完成操作流程,便证明我们的产品操作简便、易于上手。产品的易用性是我们的核心追求,若用户需要他人指导才能使用服务,那就说明我们的设计尚有不足。 - -#### 专注于高质量应用 - -在应用开发领域,Sealos 始终将质量置于数量之上。对绝大多数应用而言,稳定的运行环境及后端数据库支持是不可或缺的。我们致力于先对这些基础应用进行精细打磨,随后才拓展至其他应用领域,融合各个方向、领域的尖端应用,以便为用户提供全面且高效的解决方案。 - -#### 保障扩展性和安全性 - -Sealos 并不自己去设定标准,而是严格遵循成熟的体系和事实标准,这一策略确保了我们的服务与整个云原生生态系统的高度兼容。所有云原生应用均可在 Sealos 上安全运行,即便没有产品化的一些应用也可以通过 Sealos 的终端来运行。我们的兼容性建立在全面支持 K8s 的基础上,同时在安全性方面进行了加强。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-14-48-vbJezM.png) - -在 Sealos 中,为了避免不当操作或不适当的镜像下载对整个系统造成灾难性影响,每位用户的权限被限制在其自身的命名空间内。这种权限管理机制加强了企业级[云操作系统](https://sealos.run)的安全性与稳定性。 - -### 降低用云成本 - -我们的目标不仅是帮您降低 30% 的成本——这样的目标缺乏挑战性,过于枯燥。我们追求的是将云服务的边际成本降至极低,至少要达到原成本的十分之一。如何实现这一目标?这正是我们追求的方向。那么如何实现这个目标呢? - -#### 重定义云架构:摒弃传统模式 - -**首先,我们要摒弃传统的 IaaS、PaaS 和 SaaS 三层架构模式。** - -为何选择放弃这一经典架构?原因在于,传统的分层模式已不再符合当前的技术发展和市场需求。以 IaaS 为例,它通过软件模拟数据中心中的路由器、交换机和虚拟机等硬件,虽然提高了调度的灵活性,但同时也导致软件成本急剧增加。以 OpenStack 为例,没有数十人的团队是难以维护其稳定性的,这直接导致了高昂的软件成本。过去,这种方式似乎是提高资源利用率的必要手段,但现在从应用的角度看,许多应用在运行时并不关心它们是否运行在独立的 VPC 中。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-15-00-2jC2C8.png) - -以上是我五年前画的一张图,现在逐渐变成了现实,这与单机操作系统的发展历程类似:最初是分层的,后来逐渐发展成为更高效的内核架构。云计算的分层架构同样携带着历史的包袱。一旦企业摒弃了 IaaS,它们就可以节省大量成本,并享受到更高的性能。 - -从这个新的视角出发,我们发现,实际上并不需要 IaaS。同时,从技术角度来看,PaaS 和 SaaS 本质上是相同的,它们都是应用层面的服务,因此也无需进行过度区分。在新的云内核架构中,我们只需要有效地实现多租户之间的隔离。这并不需要复杂重量级的解决方案。例如,Sealos 提供了一种在不可信公网环境中实现多租户共享一个 K8s 集群的方式。我们利用强隔离容器 (如 Firecracker)、网络策略 (如 Cilium) 以及存储块设备隔离 (如 OpenEBS) 来实现这一目标,不仅成本更低,效果也更好。 - -![](https://cdn.jsdelivr.net/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-15-02-6N4ygp.png) - -#### 提高应用密度和调度效率 - -除非是非常重型计算密集型的应用,不然一台服务器上不跑个 100 多个应用你出门都不好意思和别人打招呼,而在 Sealos 上我们一台服务器跑 800 个应用!还能保证应用的稳定。 - -这对企业来说非常重要,企业可以显著减少对硬件资源的采购需求。如果你是企业高管,可以重新审视一下公司的整体资源利用率,你会发现大部分情况下这一比率都低于 20%。通过我们的方法,还有很多倍的提升空间。 - -通过 Sealos,企业能够以更简单的方式节省高达一半的成本。 - -#### 充分弹性 - -夜间企业的不活跃应用应该都去睡觉休息,把资源留给离线计算或者训练任务,这点其实用公有云更有优势,因为可以直接释放资源,节省大量成本。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-18-LIZ6Yt.png) - -Sealos 直接把这一重要特性内置。如果企业所有应用都以这样的方式运行,可以节省巨量的成本。 - -#### 干掉僵尸应用和僵尸服务器 - -企业里面有很多开发测试的程序,怎么知道哪些没人在用?甚至还有很多僵尸 “服务器”,有些企业只能靠一个 exel 表来维护谁用了啥,过一段时间把负责都问一遍,清退没人有的服务器,稍微高端点的会搞个老掉牙的 CMDB。。。 - -解决这个问题的釜底抽薪办法是:收钱。对,企业内部也得收钱,欠费的应用就直接干掉。 - -这样每个部门可以申请额度,开发者申请额度,额度用完就干掉应用,这样可以长期保证没有僵尸应用。而 Sealos 把所有服务器都统一纳管,整个集群变成一个大资源池,当然就不可能存在僵尸服务器了。同时还帮企业节省了一帮运维人力。 - -要想杜绝资源浪费就需要这样精细化运营,Sealos 以极低的成本达到这个目的,企业管理者唯一要做的事就是给每个子账户分钱即可。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-18-ijXZ4y.png) - -这样你可以精细化的控制到每个部门每个人用多少钱,从而进一步分析 ROI。 - -#### 最多半个人运维整个云 - -一帮运维?每个业务都有运维组?你见过微软把 PC 卖给你再给你配个运维?所以还是软件不够优秀,足够优秀足够稳定就不需要运维这个角色。或者这个角色做的事情会改变比如写编排文件写 operator。 - -Sealos 的开发者花了不到一半精力维护整个云,8000 个应用时半个人,8w 个也是 80w 个也是 800w 个也是半个人,这就是[云操作系统](https://sealos.run),不会因为体量变大而增加运维复杂度。 - -我是一个比较中性的人但是我有个极端观点是云足够成熟的情况下不应该有运维这个角色,如果你的企业运维超过 3 个人 (搬服务器的除外),那应该好好反思一下。 - -在给现在的运维人员指条明路:去开发[云操作系统](https://sealos.run)。 - -#### 研发人力成本 - -我是一个研发,我至少 50% 以上的精力花在了研发之外的事上,那些杂事加起来可能有 20% 但是其影响可能是 80% 。它会割裂我正在做的事,比如你写完代码想着还要卖服务器,配置证书,打包,上线一想到这些我敢打赌没有哪个开发者喜欢做这些事,除非他是个变态。开发者是群懒人,为了偷懒开发出一大堆工具,这是偷懒者的胜利,sealos 也是一群偷懒者创造的,所以能自动绝不手动,能 AI 绝不人工。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-18-as6HSI.png) - -自己分析问题多累,AI 比人还专业。 - -Sealos 上函数计算能力 Laf 就可以让写代码像写博客一样简单,点击保存,关机走人,要想下班早,Laf 得用好。 - -所有后端依赖如数据库这些都可以轻松 30s 之内解决。未来还有 AI 自动打包上线,自动编码调试等。 - -这无形中的研发效率提升可以节省的成本是难以想象的,我们客户例子中 2 个人干五个人活的比比皆是。 - -### 一键构建私有云,公有云私有云体验一致 - -Sealos 对云计算的理解是深刻的: - -**公有云和私有云是一回事,同一个抽象,同一套代码,同一种体验,装的应用不同尔而已** - -你会发现 linux 不管跑在你自己的数据中心还是跑在公有云上都是一种产品形态,这就是优秀软件的特点,能做到高度抽象。 - -Sealos 设计之初就考虑到这一点,其实公有云与私有云本质是一样的,都是链接计算资源。很多人可能觉得不一样啊,公有云还有充值计费什么的,其实只需要把这些功能放到一个单独的应用中即可,这样在不需要的场景直接不安装这个应用。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-18-vMGZyu.png) - -但其实大一点的企业即便做私有云它的形态也应该和公有云一样,计量计费是非常重要的一个功能,企业超过 10 个人都需要精细化运营云资源,就更别说成千上万人的企业用私有云了,各部门的成本分摊等。 - -一些非常小的差异比如微信支付第三方登录这些可能确实不太需要,这就是一些小的配置。 - -## Sealos 技术分析 - -Sealos 选择一条非常有挑战的场景:在公网不可信的环境中让多租户共享一个 K8s 集群。 - -这样做的好处是巨大的: - -- 用户进来直接用,不需要构建集群,也只需要为容器付费,成本大大降低。 -- 随着规模增大就会产生飞轮效应,让边际成本大量降低。(Sealos 摩尔定律:Sealos 集群规模每翻一倍用户用云成本会降低 30%) - -同时带来了一个巨大的技术挑战:隔离性,安全性和超大规模。 - -我们解决了这个技术挑战,那不仅在公有云上为客户提供很大价值,在私有云场景就更轻松拿捏了。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-19-sJYA9X.png) - -从这张图中拆解 Sealos 的技术体系: - -### 到处运行 - -这块比较著名的开源项目可能是 terraform 了,不过很遗憾在对接某个云时启动速度很慢 (某些 driver 写的不够好,不能怪 terraform),而且很容易触发云厂商的 API 调用 limit (也是 driver 乱请求),无法满足我们的需求。 - -而且我们希望的标准是 K8s CRD 而非 terraform,这样配合上 helm 就可以,于是我们自研了一个对接基础设施的控制器。结果是本需要 10min 启动的基础设施我们优化到了 30s,这几乎是极限了,很难在有压榨空间了,除非云厂商自己服务器启动时间能再快些。优化点主要在一些并行的处理以及退避算法调优,而不是动不动就 sleep 10s。加上在这些虚拟机上启动 Sealos 集群也只需要 3min,这已经优于很多同类产品了,大家可以自行比较 (其它一般 15min) - -同样在裸机上运行要考虑大量兼容性问题,所以 Sealos 底层几乎全部抛弃 rpm apt 这些与操作系统耦合的安装工具,这样几乎兼容了全部主流的 linux 发行版。windows 我们不支持,原因是因为单纯的讨厌不喜欢。 - -同时我们的集群镜像能力可以很好的同时支持 ARM x86 这些主流硬件体系架构。 - -### 云驱动层 - -这块挑战巨大,如果不考虑安全性隔离性,这块装一个 containerd calico openebs 可能就 OK 了,但是在公网不可信环境中这种弱隔离肯定是不行的,所以我们在一些新的技术上填坑,如 firecracker 来解决容器维度强隔离,而云厂商虚拟机里套虚拟机又是有问题的,这里后续我们会单独发一篇文章来介绍。 - -网络我们对计量和隔离的要求极高,而 calico 这些你懂的,隔离会使用大量的 iptables 规则,规模一大基本网络就不可用了,我们测试过 5000 条规则时压力测试一下就有 30% 的失败率。网络这块我们就引入了 cilium,通过 ebpf 来解决这这些问题,还有多租户的网络计量。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-19-REW9Fy.png) - -存储我们使用 openebs + lvm,为每个用户挂载独立隔离的卷,这样用户可以享受到本地磁盘的性能。而文件存储又变成一个大问题,nfs 这些几乎只是玩具,根本无法生产。所以我们世界冠军同学带队基于 rust 完全自研 sealfs 文件系统,架构超级精简,主打高性能,支持 RDMA。 - -### 生命周期管理与集群镜像 - -对于集群安装扩容 Sealos 老用户都知道,几乎已经被做到极致了,多复杂的集群一律一条命令搞定! - -而集群镜像能力世界上可能 (为了不触犯广告法加上可能二字) 只有两款软件支持 Sealos 和 sealer,都是我主导开发的,sealer 在阿里时捐给了 CNCF。这种能力是交付之王!可以用完全兼容 Docker 镜像的格式把整个集群打包,到了客户环境一键交付。 - -在集群镜像能力面前可能 (用词严谨) 其它所有交付工具我只能说:都是弟弟。 - -### 租户管理 - -任何一个企业级用户,多租户都是刚需,在设计租户权限的时候既要灵活又不能复杂,这些部门或者开发者之间既要相互隔离,又需要能相互协作,而原生的 K8s 是不帮你做这些的,只提供一个最粗略的 namespace 管理肯定是不够的。 - -Sealos 会为每个登录的用户分配一个独立的 kubeconfig,其权限只会被限制到用户自己的 namespace 中,用户可以把自己的 namespace 共享给别人。用户想穿透到主机,或者使用特权,或者共享主机文件系统端口号这些危险操作统统禁止,这才是企业实践云原生的 (可能) 最佳姿势。 - -### 应用管理 - -应用是 Sealos 的一等公民,在云内核之上,一切皆应用。这里最难的地方是在多租户的情况下如何寻求一个统一的应用管理方式的抽象。 - -比如有些应用是需要一个管理员权限跑一个控制器的。 - -有些应用是需要跑一个单独的实例的。 - -有些应用是一个实例多个用户共享使用的。如一个 chatGPT API。 - -有些应用是不使用时就要被自动释放的。如 terminal 应用,没人用会自动被回收。 - -而系统还需要对这些应用进行权限的管控和计量,进一步增加了复杂度。 - -Sealos 把这些能力全部抽象成了应用,就像你 macOS 上跑的应用一样。 - -### 自研函数计算应用 Laf - -真的让你体验什么是像写博客一样写代码 - -- 云端开发,码上生花 -- Laf 用的好,天天下班早 -- 行政都会用的云开发 -- 函数计算有两种 30s 上线的和 30s 劝退的 - -Laf 的毫秒级发布几乎吊打一切,一般其他方案发布一次都得等个 3 到 5s,而 laf 能做到比发博客还快。 - -Laf 直接集成 GPT4,大部分代码就不用你自己写了,我们训练了几千份 Laf 的代码,现在 AI 写起来已经很绝绝子了。 - -数据库内置,对象存储内置,除了用 AI 写点代码几乎不用关心其它任何东西。 - -对 websocket 的支持几乎天下无敌 (有些 function 按调用时长收费,我就问你一个长链接你的钱包还够不够) - -别的函数计算不是常驻模型,一个 chatGPT 的会话 ID 你都得存到数据库,而 Laf 完全不需要,用的是常驻自动伸缩容器。 - -### 自研 AI 知识库应用 fastGPT - -Laf AI 写代码,sealos 故障自动诊断,AI 自动上线应用,自动构建 Docker 镜像,这些统统靠 fastGPT 这个项目,自动帮你构建知识库。 - -![](https://jsd.cdn.zzko.cn/gh/yangchuansheng/imghosting-test@main/uPic/2023-11-17-16-19-cCinq3.png) - -### 数据库/消息队列等应用 - -我们核心聚焦在操作系统和部分内置应用上,数据库消息队列这些是个非常专业的领域,我们选择的办法是与顶级天团团队合作,数据库我们选用 kubeblocks,是 polarDB 创始人曹伟主导的项目 (刚好就在我们隔壁,他们的咖啡好喝,数据库好用)。kubeblocks 统一编排了 mysql pgsql mongo redis,各种高可用数据备份恢复等,绝对的为数据保驾护航。数据库管控我们与 bytebase 合作,google **云数据库团队担任技术负责人陈天舟带队。** - -消息队列我们选择与 rocketmq 创始人王小瑞合作,统一解决 rocketmq kafka 等消息队列服务。 - -devops 我们与 gitea 合作,90% 兼容 github actions,gitea 作者亲自带队,github actions 几乎是一个完美 devops 方案,gitea 选择与其兼容是一个非常明智的选择。 - -### 计量计费 - -计量并不容易,首先需要有类似监控系统的采集机制,容器的 CPU 内存磁盘,采集到了之后需要与其 namespace 关联,最终关联到账户。除此之外还需要采集一些比较难采集到的东西,比如函数计算里面数据库的访问次数,对象存储每个租户用的大小等。最难的是在不影响网络性能的情况下对网络带宽的计量。 - -计量系统的挑战是他不像监控系统那样对精准度要求不高,一旦计量出错用户的钱就出错,是个非常敏感操作,我们需要有个对账的系统对计量进行校验,确保没有收错钱。 - -有了这个能力,企业就要以对部门和内部开发者做精细化的运营,应用欠费自动停机就会让整个公司几乎没有僵尸进程。 - -我们还实现了公有云模式与私有云模式计量的统一抽象。 - -## Sealos 设计理念与原则 - -牛掰的东西从使用者视角来看一定是简单的,如苹果手机,安卓操作系统。云计算同样可以做到,但是如果简单功能不强,那叫简陋。牛逼的架构不会牺牲复杂度来增强功能。 - -Sealos 一旦违背这些原则,就会走向笨重,奔向死亡。 - -### 大道至简 - -Sealos 大道至简体现在两个方面:产品设计&系统架构。 - -产品上我们坚决不想给用户带来任何负担,就希望用户像用个人电脑一样用云,是什么样的角色关心什么样的应用,坚决不让额外你不需要用的功能对你产生干扰。这不意味着功能就弱,Sealos 可以通过扩展任何应用来增强系统的能力,也提供原生 API 来自由扩展。 - -系统架构层面抛弃 IaaS PaaS SaaS 三层架构是个明智之举,因为从应用视角来看其实压根就不需要复杂的三层架构去支撑,云内核+云驱动,**系统之上皆为应用。** - -### 化整为零 - -Sealos 可以精简到只剩一个裸 kubernetes,也可以装成千上万个应用,可以跑在电视盒子上,也可以运行在数万台服务器的数据中心,想想优秀的 linux 是不是也是这样,可以运行在嵌入式设备上也可以运行在最大的数据中心,这就是一种化整为零的架构,可以做到恰好满足你的需求,而不是堆了一大堆你不需要的能力。 - -只有这种架构才能做到无限扩展。 - -### 自由组装 - -内聚精简的架构就可以根据自己的需求来做组装,让你的云多一分则嫌多,少一分则嫌少。这得益于高度抽象的能力,具体功能通过应用本身去实现,而[云操作系统](https://sealos.run)本身对下只需要池化资源,对上只需要管理好应用即可,这样即便整个生态几十万应用出现也不会增加你的云的复杂度。参考你是怎么用智能手机的,云也可以这样玩。 - -## 使用场景 - -### 直接使用 Sealos 提供的云服务 - -- 任何的业务组件能 build 成 docker 镜像的就能很轻松跑在 Sealos 上 (后面当然会有 AI 帮助完成镜像构建,什么 Docker 我不懂),比如企业内部各种编程语言写的项目。 -- 四大数据库高可用集群一键启动 pgsql/mysql/mongo/redis,备份/恢复/监控/管控应有仅有。 -- 各种知名开源项目都可运行在 Sealos 上。 - -所以可以很好的在 Sealos 上运行你的业务系统,解决了业务运行时问题和所有后端依赖。 - -### 帮助你构建完整的私有云 - -大家可能越来越发现裸硬件不仅比虚拟机性能更好,而且价格还便宜,不过还是大量公司还不会考虑托管硬件,原因就在于很难搞得定软件部分,搭 openstack?搭 kubernetes?其实都差点意思。 - -Sealos 的云服务版本可以一模一样的 clone 到你自己的机房,Sealos 目前已经服务数万在线用户了,能支持的场景和复杂度已经超过绝大多数公司了,毕竟有几万开发者的公司还是不多的。 - -所以裸机+Sealos 软硬件都有了,自建私有云这个事就成立了。 - -自建成本有多高呢? - -- 买好服务器 -- 一条命令起集群,小白都会,管你多少台服务器都是一键 -- 最多 0.5 人维护,我们基本实现了自运维,目前在线集群服务上万应用投入的维护人力 0.1 个左右 - -## 与其他平台的比较 - -### 对比其他云原生 PaaS 平台 - -这是问的最多的,最大的区别是设计理念,Sealos 不会去做一个大而全的 PaaS 平台,[云操作系统](https://sealos.run)本身是高度抽象的,是 nothing,Sealos 上应用是一等公民,通过各种不同的应用来满足用户的需求,比如 Sealos 上 Database 应用,你在使用它时完全不用关心其他任何概念,kubernetes 单词怎么拼都不知道。 - -与追求大而全的传统 PaaS 平台不同,Sealos 把[云操作系统](https://sealos.run)视为高度抽象化的 “nothing”,强调应用程序的重要性。在 Sealos 平台上,**应用被视为第一等公民**,通过多样化的应用来满足用户的各种需求。例如,使用 Sealos 的数据库应用时,用户无需关注任何其他概念,**甚至不需要知道如何拼写 “Kubernetes”**。 - -Rancher 和 KubeSphere 是非常优秀的 PaaS 平台。但 Sealos 并不将 K8s 作为其核心目的。它更注重于 K8s 上运行的应用,而非 K8s 本身。因此,Sealos 的目标用户是广泛的开发者群体,其意图是打造一个通用性操作系统,不局限于仅服务于云原生领域。Sealos 甚至都不太想强调 “云原生” 这一尚未明确定义的概念。 - -因此,Sealos 的核心思想并非是 “更好用的 Kubernetes”,而是 “**利用 K8s 为用户提供所需的应用**”。 - -> 用户真正需要的是什么? -> - -在操作系统领域,用户的需求定义了系统的功能。操作系统的灵活性意味着它不会给用户带来额外负担。例如,Windows 对于游戏玩家而言是游戏平台,对程序员则是编程工具,对美工来说则是图像处理软件。操作系统的形态由使用者决定,取决于装载了哪些应用。Sealos 也秉承这样的设计理念,因此不同用户的体验将截然不同。 - -### 对比各种 K8s 安装工具 - -安装只是整个操作系统的一个 boot 功能,不过 Sealos 在集群生命周期管理和应用打包交付上也有极大的特色。 - -首先,Sealos 通过一条命令即可完成安装。其次,利用集群镜像,可以将整个集群打包并随处交付。最后,Sealos 允许用户像编写 Dockerfile 一样灵活地定制所需的集群,自由组装和替换镜像中的组件,提供了上百种组件供用户选择。 - -## 总结 - -Sealos 社区现在拥有庞大的社区用户基础,发展了很多年,久经沙场,稳定性在各种极端场景下久经考验,稳如老狗。 - -我们云服务注册用户和应用数量也在夸张级别的增长,上线两周超 6k 在线开发者,近万应用数量。 - -我们会为用户提供一个公有云私有云体验完全一致,简单,便宜,开放,强大的[云操作系统](https://sealos.run)。 \ No newline at end of file diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/access-meilisearch-on-sealos.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/access-meilisearch-on-sealos.jpg deleted file mode 100644 index a522b7e4f4f..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/access-meilisearch-on-sealos.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/change-meilisearch-mem-on-sealos.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/change-meilisearch-mem-on-sealos.jpg deleted file mode 100644 index 3c01b896c94..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/change-meilisearch-mem-on-sealos.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/deploy-meilisearch-on-sealos.png b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/deploy-meilisearch-on-sealos.png deleted file mode 100644 index 99e9b0abe16..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/deploy-meilisearch-on-sealos.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/details-of-meilisearch-on-sealos.png b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/details-of-meilisearch-on-sealos.png deleted file mode 100644 index b309fd40880..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/details-of-meilisearch-on-sealos.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/feature.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/feature.jpg deleted file mode 100644 index ac9764aaf63..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/feature.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/flarum-admin-panel.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/flarum-admin-panel.jpg deleted file mode 100644 index 9b8833e4a4a..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/flarum-admin-panel.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/flarum-integrate-meilisearch.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/flarum-integrate-meilisearch.jpg deleted file mode 100644 index 4a701e0a3e9..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/flarum-integrate-meilisearch.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/import-flarum-index-to-meilisearch.png b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/import-flarum-index-to-meilisearch.png deleted file mode 100644 index 833951d0140..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/import-flarum-index-to-meilisearch.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop-2.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop-2.jpg deleted file mode 100644 index 141200ee201..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop-2.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop.jpg deleted file mode 100644 index 40fbe13567a..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-on-sealos-desktop.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-private-address-on-sealos.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-private-address-on-sealos.jpg deleted file mode 100644 index 1556c8f9943..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/meilisearch-private-address-on-sealos.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/open-meilisearch-terminal-on-sealos.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/open-meilisearch-terminal-on-sealos.jpg deleted file mode 100644 index 94f537cf841..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/open-meilisearch-terminal-on-sealos.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/public-address-of-meilisearch-on-sealos.png b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/public-address-of-meilisearch-on-sealos.png deleted file mode 100644 index 1ce6576c94a..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/public-address-of-meilisearch-on-sealos.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/scout-extension-on-flarum.jpg b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/scout-extension-on-flarum.jpg deleted file mode 100644 index 0db407e505b..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/scout-extension-on-flarum.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/star-history-of-meilisearch.png b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/star-history-of-meilisearch.png deleted file mode 100644 index df050313a35..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/star-history-of-meilisearch.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/update-meilisearch-on-sealos.png b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/update-meilisearch-on-sealos.png deleted file mode 100644 index ab02ae73f9e..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/images/update-meilisearch-on-sealos.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/index.md b/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/index.md deleted file mode 100644 index 36f4cfb883c..00000000000 --- a/docs/blog/zh-Hans/2024/how-to-deploy-and-configure-meilisearch-using-docker/index.md +++ /dev/null @@ -1,294 +0,0 @@ ---- -slug: how-to-deploy-and-configure-meilisearch-using-docker -title: "Elasticsearch 替代品 Meilisearch 安装和使用教程" -description: 本教程探讨了 Meilisearch 的核心特性、安装配置过程,以及如何将其集成到实际应用中。 -authors: [Carson Yang] -tags: [Developer Tools, Sealos, Meilisearch, Flarum] -keywords: [Meilisearch, 搜索引擎, 搜索, 开源, Flarum, Elasticsearch 替代, 搜索引擎对比] -image: ./images/feature.jpg -date: 2024-07-01T10:00 ---- - -如今搜索功能已成为几乎所有应用不可或缺的一部分。无论是电商平台、内容管理系统,还是企业内部知识库,用户都期待能够快速、准确地找到他们需要的信息。然而,传统的搜索解决方案往往面临着诸多挑战:响应速度慢、相关性差、难以适应大规模数据、缺乏灵活性等。这些问题不仅影响用户体验,还可能导致用户流失,最终影响业务增长。 - -为了解决这些问题,我们将为您介绍一款强大而灵活的开源搜索引擎 - [Meilisearch](https://www.meilisearch.com/)。作为一个使用 [Rust](https://www.rust-lang.org/) 语言编写的独立搜索引擎,Meilisearch 以其简单的部署流程、快速的查询响应和丰富的功能集而著称。您只需一个命令行二进制文件,就能运行 Meilisearch 服务器并开始查询,大大简化了搜索引擎的使用门槛。它不仅支持模糊匹配和无模式索引等先进特性,还提供了用于演示目的的 Web 前端界面。 - - - -在本教程中,我们将深入探讨 Meilisearch 的核心优势和实际应用。我们将涵盖以下内容: - -+ Meilisearch 的核心特性和优势概述 -+ 如何安装和配置 Meilisearch -+ Meilisearch 的基本使用方法 -+ 将 Meilisearch 集成到实际应用中(以 Flarum 论坛为例) -+ Meilisearch 与其他搜索解决方案的比较 - -## 先决条件 - -在开始之前,请确保您具备以下条件: - -- 基本的命令行操作知识 -- 对 RESTful API 的基本理解 -- (可选)一个 [Sealos](https://sealos.run) 账户,用于快速部署 Meilisearch - -## Meilisearch 简介 - -[Meilisearch](https://github.com/meilisearch/meilisearch) 是一个使用 **Rust 语言编写**的强大开源搜索引擎。它提供了闪电般快速的全文搜索功能,并且易于使用和集成。Meilisearch 的设计理念围绕着以下核心原则: - -1. **速度至上**:在任何规模的数据集上,都能在 50 毫秒内返回结果。 -2. **相关性优先**:通过智能排序算法,确保最相关的结果总是排在前面。 -3. **开发者友好**:提供直观的 API 和丰富的文档,大大降低了集成和使用的门槛。 -4. **高度可定制**:灵活的配置选项,允许开发者根据具体需求调整搜索行为。 - -自 2018 年首次发布以来,Meilisearch 凭借其简单易用的特性和卓越的性能,迅速在开源社区中脱颖而出。目前,它在 GitHub 上已经获得了超过 40,000 颗星。 - -![开源项目 Meilisearch 的 Star History](./images/star-history-of-meilisearch.png) - -## Meilisearch 的核心特性 - -Meilisearch 提供了丰富多样的搜索功能,可以满足从个人项目到大型企业应用的各种需求。以下是其核心特性: - -### 搜索性能 - -- **闪电般的速度**:无论数据集大小如何,都能在 50 毫秒内返回结果。 -- **即时搜索**:支持 "搜索即输入" (Search-as-you-type) 功能,提供实时反馈。 -- **拼写容错**:智能处理拼写错误,即使查询中存在错误也能返回相关结果。 - -### 相关性优化 - -- **自定义排序**:允许根据业务需求自定义搜索结果的排序规则。 -- **分面搜索**:支持多维度的结果过滤和导航。 -- **同义词管理**:可以设置同义词,提高搜索的灵活性。 - -### 多语言支持 - -- **多语言优化**:针对多种语言进行了优化,包括中文、日文等非拉丁语系。 -- **停用词处理**:可配置停用词列表,忽略对搜索结果影响不大的常见词。 - -### 高级功能 - -- **地理位置搜索**:支持基于地理位置的搜索和排序。 -- **多租户支持**:通过租户令牌实现数据隔离和访问控制。 -- **高亮显示**:在搜索结果中高亮显示匹配的文本。 -- **文档管理**:支持添加、更新和删除索引中的文档。 - -### 开发者友好 - -- **RESTful API**:提供简洁明了的 API,易于集成。 -- **多语言 SDK**:官方提供多种编程语言的 SDK。 -- **详细文档**:提供全面的文档和示例。 -- **可自托管**:支持在自己的基础设施上部署和管理。 - -## Meilisearch vs. 其他搜索解决方案 - -为了更直观地展示 Meilisearch 的优势,我们可以将其与市面上的其他主流搜索解决方案进行对比: - -| 特性 | Meilisearch | Elasticsearch | Algolia | -| ------------ | -------------- | ---------------------------- | --------- | -| 响应速度 | <50ms | 因环境和配置而异,通常>100ms | <100ms | -| 易用性 | 高 | 中 | 高 | -| 自动错误纠正 | 是 | 需配置 | 是 | -| 多语言支持 | 优秀 | 良好 | 优秀 | -| 地理位置搜索 | 支持 | 支持 | 支持 | -| 开源 | 是 | 是(部分功能闭源) | 否 | -| 定价 | 免费(自托管) | 免费(自托管),付费云服务 | 付费 SaaS | - -虽然 Elasticsearch 在功能全面性和生态系统方面略胜一筹,Algolia 则在云服务和开箱即用体验上有优势,但 Meilisearch 在速度、易用性和开源友好度上独树一帜。特别是对于中小型项目和希望完全控制搜索基础设施的团队来说,Meilisearch 提供了一个完美的平衡点。 - -## Meilisearch 的安装和配置 - -Meilisearch 的安装和使用相对简单,提供了多种安装方式以适应不同的环境和需求。 - -对于没有技术背景的同学而言,你也不用担心安装问题,[Sealos 应用商店](https://sealos.run/docs/guides/templates/)提供了一键部署的应用模板,点一下鼠标即可完成部署,非常丝滑。 - -**如果你想快速部署一个 Meilisearch,又不想陷入繁琐的安装和配置过程**,可以试试 Sealos。 - -直接打开 [Meilisearch 应用模板](https://template.bja.sealos.run/deploy?templateName=meilisearch),然后点击右上角的 “去 Sealos 部署”。 - -> 如果您是第一次使用 [Sealos](https://sealos.run/),则需要注册登录 Sealos 公有云账号,登录之后会立即跳转到模板的部署页面。 - -这里有两个关键的环境变量需要特别注意一下,正确配置这些变量可以确保您的 Meilisearch 的安全和性能。 - -1. **MEILI_ENV**:用于配置实例的环境,只能是 **production** 或 **development**。 - - production 模式: - - - 禁用了搜索预览界面。 - - development 模式: - - - 启用了搜索预览功能。 - -2. **MEILI_MASTER_KEY**:用于设置 Meilisearch 的主密钥,它会自动保护除 `GET /health` 之外的所有路由。只有通过 API 密钥才能访问其他端点,包括搜索预览界面。 - - 在 production 模式下: - - - 提供主密钥是强制性的。 - - 如果没有提供主密钥或密钥长度小于 16 字节,Meilisearch 将抛出错误并拒绝启动。 - - 在 development 模式下: - - - 提供主密钥是可选的。 - - 如果没有提供主密钥,所有路由将不受保护且可公开访问。 - -无论在哪种模式下,如果你没有提供主密钥或提供的密钥长度小于 16 字节,Meilisearch 都会建议你使用一个自动生成的主密钥。 - -总结一下:**如果是 production 模式,则必须要提供主密钥;如果是 development 模式,则可以不提供主密钥。** - -密钥的生成非常简单,直接在 Linux 或者 macOS 终端里执行以下命令即可生成一个随机密钥: - -```bash -openssl rand -base64 48 -``` - -填好参数之后,点击右上角的 “部署应用” 开始部署。 - -![Deploy Meilisearch on Sealos](./images/deploy-meilisearch-on-sealos.png) - -部署完成后,直接点击应用的 “详情” 进入该应用的详情页面。 - -![Details of Meilisearch on Sealos](./images/details-of-meilisearch-on-sealos.png) - -等待应用状态变成 running 之后,直接点击外网地址便可打开 Meilisearch 的搜索预览界面。 - -![Public address of Meilisearch on Sealos](./images/public-address-of-meilisearch-on-sealos.png) - -打开之后是这个样子的,输入你设置的密钥,就可以访问了。 - -![Access Meilisearch on Sealos](./images/access-meilisearch-on-sealos.jpg) - -除此之外,还有另外一种打开方式,先刷新 Sealos 桌面 (也就是在 [cloud.sealos.run](https://cloud.sealos.run) 界面刷新浏览器),然后你就会发现 Sealos 桌面多了个图标: - -![Meilisearch on Sealos Desktop](./images/meilisearch-on-sealos-desktop.jpg) - -直接点击这个图标就可以打开 Meilisearch 的搜索预览界面了。 - -![Meilisearch on Sealos Desktop](./images/meilisearch-on-sealos-desktop-2.jpg) - -是不是有点似曾相识?没错,很像 **Windows 的快捷方式!** - -单机操作系统可以这么玩,Sealos 云操作系统当然也可以这么玩。 - -## Meilisearch 的基本使用 - -Meilisearch 提供了直观的 RESTful API,使得与各种编程语言和框架的集成变得非常简单。以下是一些基本操作示例: - -### 1. 创建索引 - -```bash -$ curl \ - -X POST 'http://localhost:7700/indexes' \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer YOUR_API_KEY' \ - --data-binary '{ - "uid": "movies", - "primaryKey": "id" - }' -``` - -### 2. 添加文档 - -```bash -$ curl \ - -X POST 'http://localhost:7700/indexes/movies/documents' \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer YOUR_API_KEY' \ - --data-binary '[ - { - "id": 1, - "title": "Carol", - "genres": ["Romance", "Drama"] - }, - { - "id": 2, - "title": "Wonder Woman", - "genres": ["Action", "Adventure"] - } - ]' -``` - -### 3. 搜索文档 - -```bash -$ curl \ - -X POST 'http://localhost:7700/indexes/movies/search' \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer YOUR_API_KEY' \ - --data-binary '{ - "q": "wonder" - }' -``` - -## 将 Meilisearch 集成到 Flarum 论坛 - -为了展示 Meilisearch 在实际应用中的强大功能,我们将以 Flarum 论坛为例,演示如何接入 Meilisearch 来提升搜索能力。 - -> 关于 Flarum 的安装和使用方法,可参考我们之前写的文章:[Flarum 安装和使用教程](https://zhuanlan.zhihu.com/p/703201822) - -### 1. 安装 Meilisearch SDK - -在 Flarum 应用详情界面,点击右下角的终端按钮: - -![在 Sealos 中打开 Meilisearch 应用终端](./images/open-meilisearch-terminal-on-sealos.jpg) - -在打开的终端中执行以下命令安装 Meilisearch SDK: - -```bash -extension require meilisearch/meilisearch-php -``` - -### 2. 安装 Scout Search 扩展 - -和上面一样,在 Flarum 容器终端中执行以下命令安装 Scout Search 扩展: - -```bash -extension require clarkwinkelmann/flarum-ext-scout -``` - -### 3. 启用 Scout 扩展 - -在 Flarum 管理后台中启用 Scout 扩展,并选择 Meilisearch 作为搜索引擎,index 名称可写可不写。 - -![Flarum admin panel](./images/flarum-admin-panel.jpg) - -下面还需要配置 Meilisearch 的 URL 和密钥。 - -![Scout extension on flarum](./images/scout-extension-on-flarum.jpg) - -如果你的 Meilisearch 和 Flarum 部署在同一个 Sealos 可用区,那么可以直接使用 Meilisearch 的内网地址,进入 Meilisearch 的应用详情页面,点击内网地址便可复制 Meilisearch 的内网地址,然后作为上面 Scout 插件 Meilisearch Host 的值粘贴进去即可。 - -![Meilisearch private address on Sealos](./images/meilisearch-private-address-on-sealos.jpg) - -### 4. 增加 Meilisearch 内存 - -Sealos 中部署的 Meilisearch 默认只给了 128M 内存,如果要接入 Flarum,这个内存是不够用的,需要调整到 1G 才够用。操作方法如下: - -进入 Meilisearch 的应用详情页面,点击右上角的 “变更”: - -![Update Meilisearch on Sealos](./images/update-meilisearch-on-sealos.png) - -将内存调整到 1G,然后点击右上角的 “变更” 即可。 - -![Change Meilisearch memory on Sealos](./images/change-meilisearch-mem-on-sealos.jpg) - -### 5. 导入 Meilisearch 索引 - -使用以下命令将现有数据导入 Meilisearch 索引: - -```bash -php flarum scout:import-all -``` - -下面这种结果就是导入成功了: - -![Import flarum index to Meilisearch](./images/import-flarum-index-to-meilisearch.png) - -最后来测试一下中文搜索功能: - -![Flarum integrate Meilisearch](./images/flarum-integrate-meilisearch.jpg) - -## 总结 - -Meilisearch 为开发者提供了一个强大而灵活的搜索解决方案,特别适合那些需要快速、相关且易于实现的搜索功能的应用。通过本教程,我们探讨了 Meilisearch 的核心特性、安装配置过程,以及如何将其集成到实际应用中。 - -我们鼓励您进一步探索 Meilisearch 的高级功能,如自定义排名规则、同义词设置等,以充分发挥其潜力。同时,关注 Meilisearch 的官方文档和社区更新,以获取最新的功能和最佳实践。 \ No newline at end of file diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-console.png b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-console.png deleted file mode 100644 index e8295821638..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-console.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-console2.png b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-console2.png deleted file mode 100644 index dcb1c5a2143..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-console2.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-details.png b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-details.png deleted file mode 100644 index 7059e69d704..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-details.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-logs.png b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-logs.png deleted file mode 100644 index 82ffc311092..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-logs.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-on-sealos.png b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-on-sealos.png deleted file mode 100644 index 8164c91c838..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-on-sealos.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-on-sealos2.png b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-on-sealos2.png deleted file mode 100644 index 98af4a97cba..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-on-sealos2.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-whiteboard.png b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-whiteboard.png deleted file mode 100644 index dcc99fe0837..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-whiteboard.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-workspace.png b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-workspace.png deleted file mode 100644 index f40ff9204f4..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE-workspace.png and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE.jpg b/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE.jpg deleted file mode 100644 index 8fea1fd20f1..00000000000 Binary files a/docs/blog/zh-Hans/2024/how-to-set-up-affine/images/AFFINE.jpg and /dev/null differ diff --git a/docs/blog/zh-Hans/2024/how-to-set-up-affine/index.md b/docs/blog/zh-Hans/2024/how-to-set-up-affine/index.md deleted file mode 100644 index 66927c6f597..00000000000 --- a/docs/blog/zh-Hans/2024/how-to-set-up-affine/index.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -slug: how-to-set-up-affine -title: Notion 开源替代品 AFFINE 部署和使用教程 -description: 本文介绍了开源知识库管理工具 AFFINE 的私有化部署和使用。 -authors: [Carson Yang] -tags: [Notes, Sealos, Wiki] -keywords: [AFFINE, Sealos, Notion, Notes, Wiki, whiteboard] -image: images/feature.jpg -date: 2024-04-07T10:00 ---- - -AFFiNE 是一款完全开源的 Notion + Miro 替代品,与 Notion 相比,AFFiNE 更注重隐私安全,优先将笔记内容保存到本地。 - - - -GitHub 地址:[https://github.com/toeverything/AFFiNE](https://github.com/toeverything/AFFiNE) - -AFFiNE 使用 Rust 和 Typescript 构建,只需要一个命令即可运行整个项目,AFFiNE 以最简单的方式给了所有开发者最大的想象力。 - -与专注于白板和页面的 Miro 和 Notion 不同,AFFiNE 将其定位定义为一体化的 KnowledgeOS。它支持看板、表格和富文本段落作为构建块来形成页面或白板,可以在这里进行文档编辑、数据处理或头脑风暴等综合处理。 - -![](./images/AFFINE.jpg) - -## AFFiNE 的特性 - -### 文档与白板融为一体 - -许多编辑应用声称自己是提高生产力的画布,但 AFFiNE 是为数不多的几款可以让你在无边界画布上放置任何构建块的应用之一 -- 富文本、便签、任何嵌入的网页、多视图数据库、链接页面甚至幻灯片。 - -每个页面都有 2 个视图,你可以在任何地方以任何形式访问和编辑功能齐全的块。 - -![](./images/AFFINE-whiteboard.png) - -### 多模态 AI copilot - -无论是撰写专业的工作报告,还是将大纲转化为富有表现力的幻灯片,亦或是将文章总结为结构良好的思维导图,又或者……只需一个提示,即可直接绘制和编写原型应用和网页,AFFiNE AI 都可以做到。 - -### 本地优先 & 实时协作 - -AFFiNE 遵从本地优先的理念,强调数据的私有性,你完全可以不用它的云服务,自己进行同步。用户有了更多的选择权。 - -![](./images/AFFINE-workspace.png) - -### 私有化部署 - -用户可以随意分叉和构建自定义的 AFFiNE,也可以私有化部署。未来还会推出插件社区和第三方模块。 - -## 私有化部署 AFFiNE - -AFFiNE 的私有化部署依赖 PostgreSQL 和 Redis 数据库,部署起来比较复杂。[Sealos 的应用商店](https://sealos.run/docs/guides/templates/)提供了一键部署的应用模板,点一下鼠标即可完成部署,非常丝滑。 - -直接打开这个链接: - -[![](https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://bja.sealos.run/?openapp=system-template%3FtemplateName%3Daffine) - -接下来你只需要设置一下管理员的邮箱(AFFINE_ADMIN_EMAIL)和密码(AFFINE_ADMIN_PASSWORD),然后点击右上角的「去 Sealos 部署」。 - -> 如果您是第一次使用 [Sealos](https://sealos.run),则需要注册登录 Sealos 公有云账号,登录之后会立即跳转到模板的部署页面。 - -跳转进来之后,点击右上角的「部署应用」开始部署,部署完成后,直接点击应用的「详情」进入该应用的详情页面。 - -![](./images/AFFINE-on-sealos.png) - -等待实例的状态变成 running 后,点击日志图标查看日志: - -![](./images/AFFINE-on-sealos2.png) - -如果出现了下面的日志,就说明启动成功了: - -![](./images/AFFINE-logs.png) - -这时点击外网地址即可打开 AFFINE 的可视化界面: - -![](./images/AFFINE-details.png) - -打开之后,默认使用的是本地浏览器存储。如果想登录我们自己部署的云服务,需要点击右上角的「登录并启用」: - -![](./images/AFFINE-console.png) - -登录之后,还要再点击「启用 AFFINE Cloud 服务」,不然数据还是继续存储在本地浏览器中,容易丢失: - -![](./images/AFFINE-console2.png) - -启用之后,我们就可以愉快地使用 AFFINE 啦。 \ No newline at end of file diff --git a/docs/blog/zh-Hans/authors.yml b/docs/blog/zh-Hans/authors.yml deleted file mode 100644 index 1a9418e77f7..00000000000 --- a/docs/blog/zh-Hans/authors.yml +++ /dev/null @@ -1,18 +0,0 @@ -fanux: - name: fanux - title: '@sealos' - url: https://github.com/fanux - image_url: https://avatars.githubusercontent.com/u/8912557?v=4 - -xiao-jay: - name: xiao-jay - title: '@sealos' - url: https://github.com/xiao-jay - image_url: https://avatars.githubusercontent.com/u/87080562?v=4 - -Carson Yang: - name: Carson Yang - title: '@sealos' - url: https://github.com/yangchuansheng - image_url: https://avatars.githubusercontent.com/u/15308462?v=4 - diff --git a/docs/blog/zh-Hans/options.json b/docs/blog/zh-Hans/options.json deleted file mode 100644 index c57e381cb31..00000000000 --- a/docs/blog/zh-Hans/options.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "title": { - "message": "博客", - "description": "The title for the blog used in SEO" - }, - "description": { - "message": "Sealos 云操作系统正式发布!", - "description": "The description for the blog used in SEO" - }, - "sidebar.title": { - "message": "最近的博客", - "description": "The label for the left sidebar" - } -} \ No newline at end of file diff --git a/docs/website/docusaurus.config.js b/docs/website/docusaurus.config.js index 94d64fb3527..3a12ff3ab07 100644 --- a/docs/website/docusaurus.config.js +++ b/docs/website/docusaurus.config.js @@ -75,32 +75,34 @@ const config = { themeConfig: { // @type {import('@docusaurus/preset-classic').ThemeConfig} metadata: [{ name: 'title', content: 'Sealos by 环界云' }], - announcementBar: { - id: 'sealos_tip', - content: ` -
-
${isDomesticSite ? 'If you are an international user, please visit 👉' : '如果您是国内用户,请直接访问 👉 '}
-
- ${isDomesticSite ? 'International Site' : '国内官网'} + ...(isDomesticSite ? {} : { + announcementBar: { + id: 'sealos_tip', + content: ` +
+
如果您是国内用户,请直接访问 👉
+
+ 国内官网 +
+ + + + + + + + + + + +
- - - - - - - - - - - - -
- `, - isCloseable: true, - }, + `, + isCloseable: true, + } + }), algolia: { // Algolia 提供的应用 ID appId: "SLTSB7B9Y0", diff --git a/docs/website/package-lock.json b/docs/website/package-lock.json new file mode 100644 index 00000000000..cca3c3f30d1 --- /dev/null +++ b/docs/website/package-lock.json @@ -0,0 +1,14317 @@ +{ + "name": "sealos-site", + "version": "5.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sealos-site", + "version": "5.0.0", + "dependencies": { + "@docusaurus/core": "^2.4.3", + "@docusaurus/preset-classic": "^2.4.3", + "@docusaurus/theme-search-algolia": "^2.4.3", + "@headlessui/react": "^1.7.17", + "@mdx-js/react": "^1.6.22", + "autoprefixer": "^10.4.16", + "docusaurus-plugin-sass": "^0.2.2", + "dotenv": "^16.3.1", + "postcss": "^8.4.31", + "prism-react-renderer": "^1.3.5", + "prismjs": "^1.29.0", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-helmet": "^6.1.0", + "sass": "^1.62.1", + "tailwindcss": "^3.3.3", + "wowjs": "^1.1.3" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "^2.4.3", + "@tsconfig/docusaurus": "^1.0.6", + "babel-plugin-prismjs": "^2.1.0", + "typescript": "^5.0.4" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.8.2.tgz", + "integrity": "sha512-mTeshsyFhAqw/ebqNsQpMtbnjr+qVOSKXArEj4K0d7sqc8It1XD0gkASwecm9mF/jlOQ4Z9RNg1HbdA8JPdRwQ==", + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.8.2" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.8.2.tgz", + "integrity": "sha512-J0oTx4me6ZM9kIKPuL3lyU3aB8DEvpVvR6xWmHVROx5rOYJGQcZsdG4ozxwcOyiiu3qxMkIbzntnV1S1VWD8yA==", + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.8.2" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.8.2.tgz", + "integrity": "sha512-b6Z/X4MczChMcfhk6kfRmBzPgjoPzuS9KGR4AFsiLulLNRAAqhP+xZTKtMnZGhLuc61I20d5WqlId02AZvcO6g==", + "license": "MIT" + }, + "node_modules/@algolia/cache-browser-local-storage": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.17.1.tgz", + "integrity": "sha512-e91Jpu93X3t3mVdQwF3ZDjSFMFIfzSc+I76G4EX8nl9RYXgqcjframoL05VTjcD2YCsI18RIHAWVCBoCXVZnrw==", + "license": "MIT", + "dependencies": { + "@algolia/cache-common": "4.17.1" + } + }, + "node_modules/@algolia/cache-common": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.17.1.tgz", + "integrity": "sha512-fvi1WT8aSiGAKrcTw8Qg3RYgcwW8GZMHcqEm4AyDBEy72JZlFBSY80cTQ75MslINjCHXLDT+9EN8AGI9WVY7uA==", + "license": "MIT" + }, + "node_modules/@algolia/cache-in-memory": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.17.1.tgz", + "integrity": "sha512-NbBt6eBWlsXc5geSpfPRC5dkIB/0Ptthw8r0yM5Z7D3sPlYdnTZSO9y9XWXIptRMwmZe4cM8iBMN8y0tzbcBkA==", + "license": "MIT", + "dependencies": { + "@algolia/cache-common": "4.17.1" + } + }, + "node_modules/@algolia/client-account": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.17.1.tgz", + "integrity": "sha512-3rL/6ofJvyL+q8TiWM3qoM9tig+SY4gB1Vbsj+UeJPnJm8Khm+7OS+r+mFraqR6pTehYqN8yGYoE7x4diEn4aA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.17.1", + "@algolia/client-search": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/@algolia/client-account/node_modules/@algolia/client-common": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.1.tgz", + "integrity": "sha512-+r7kg4EgbFnGsDnoGSVNtXZO8xvZ0vzf1WAOV7sqV9PMf1bp6cpJP/3IuPrSk4t5w2KVl+pC8jfTM7HcFlfBEQ==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/@algolia/client-account/node_modules/@algolia/client-search": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.1.tgz", + "integrity": "sha512-Q5YfT5gVkx60PZDQBqp/zH9aUbBdC7HVvxupiHUgnCKqRQsRZjOhLest7AI6FahepuZLBZS62COrO7v+JvKY7w==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.17.1", + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.17.1.tgz", + "integrity": "sha512-Bepr2w249vODqeBtM7i++tPmUsQ9B81aupUGbDWmjA/FX+jzQqOdhW8w1CFO5kWViNKTbz2WBIJ9U3x8hOa4bA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.17.1", + "@algolia/client-search": "4.17.1", + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/@algolia/client-analytics/node_modules/@algolia/client-common": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.1.tgz", + "integrity": "sha512-+r7kg4EgbFnGsDnoGSVNtXZO8xvZ0vzf1WAOV7sqV9PMf1bp6cpJP/3IuPrSk4t5w2KVl+pC8jfTM7HcFlfBEQ==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/@algolia/client-analytics/node_modules/@algolia/client-search": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.1.tgz", + "integrity": "sha512-Q5YfT5gVkx60PZDQBqp/zH9aUbBdC7HVvxupiHUgnCKqRQsRZjOhLest7AI6FahepuZLBZS62COrO7v+JvKY7w==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.17.1", + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.18.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.17.1.tgz", + "integrity": "sha512-gJku9DG/THJpfsSlG/az0a3QIn+VVff9kKh8PG8+7ZfxOHS+C+Y5YSeZVsC+c2cfoKLPo3CuHIiJ/p86erR3bA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.17.1", + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/@algolia/client-personalization/node_modules/@algolia/client-common": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.1.tgz", + "integrity": "sha512-+r7kg4EgbFnGsDnoGSVNtXZO8xvZ0vzf1WAOV7sqV9PMf1bp6cpJP/3IuPrSk4t5w2KVl+pC8jfTM7HcFlfBEQ==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.18.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.18.0", + "@algolia/requester-browser-xhr": "5.18.0", + "@algolia/requester-fetch": "5.18.0", + "@algolia/requester-node-http": "5.18.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==", + "license": "MIT" + }, + "node_modules/@algolia/logger-common": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.17.1.tgz", + "integrity": "sha512-Us28Ot+fLEmX9M96sa65VZ8EyEEzhYPxfhV9aQyKDjfXbUdJlJxKt6wZpoEg9RAPSdO8IjK9nmuW2P8au3rRsg==", + "license": "MIT" + }, + "node_modules/@algolia/logger-console": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.17.1.tgz", + "integrity": "sha512-iKGQTpOjHiE64W3JIOu6dmDvn+AfYIElI9jf/Nt6umRPmP/JI9rK+OHUoW4pKrBtdG0DPd62ppeNXzSnLxY6/g==", + "license": "MIT", + "dependencies": { + "@algolia/logger-common": "4.17.1" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.18.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.18.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-common": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.17.1.tgz", + "integrity": "sha512-HggXdjvVFQR0I5l7hM5WdHgQ1tqcRWeyXZz8apQ7zPWZhirmY2E9D6LVhDh/UnWQNEm7nBtM+eMFONJ3bZccIQ==", + "license": "MIT" + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.18.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.18.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.18.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.18.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/transporter": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.17.1.tgz", + "integrity": "sha512-ZM+qhX47Vh46mWH8/U9ihvy98HdTYpYQDSlqBD7IbiUbbyoCMke+qmdSX2MGhR2FCcXBSxejsJKKVAfbpaLVgg==", + "license": "MIT", + "dependencies": { + "@algolia/cache-common": "4.17.1", + "@algolia/logger-common": "4.17.1", + "@algolia/requester-common": "4.17.1" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz", + "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz", + "integrity": "sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.0", + "@babel/helper-compilation-targets": "^7.22.1", + "@babel/helper-module-transforms": "^7.22.1", + "@babel/helpers": "^7.22.0", + "@babel/parser": "^7.22.0", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz", + "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz", + "integrity": "sha512-ahEoxgqNoYXm0k22TvOke48i1PkavGu0qGCmcq9ugi6gnmvKNaMjKBSrZTnWUi1CFEeNAUiVba0Wtzm03aSkJg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz", + "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.0", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz", + "integrity": "sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.22.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.22.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz", + "integrity": "sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.3.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz", + "integrity": "sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz", + "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz", + "integrity": "sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz", + "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==", + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-simple-access": "^7.21.5", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", + "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz", + "integrity": "sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-member-expression-to-functions": "^7.22.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", + "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", + "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.3.tgz", + "integrity": "sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/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==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/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==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/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==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/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==", + "license": "MIT" + }, + "node_modules/@babel/highlight/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==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/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==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/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==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", + "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz", + "integrity": "sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-transform-optional-chaining": "^7.22.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", + "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.3.tgz", + "integrity": "sha512-i35jZJv6aO7hxEbIWQ41adVfOzjm9dcYDNeWlBMd8p0ZQRtNUCBrmGwZt+H5lb+oOC9a3svp956KP0oWGA1YsA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz", + "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.3.tgz", + "integrity": "sha512-36A4Aq48t66btydbZd5Fk0/xJqbpg/v4QWI4AH4cYHBXy9Mu42UOupZpebKFiCFNT9S9rJFcsld0gsv0ayLjtA==", + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.3.tgz", + "integrity": "sha512-mASLsd6rhOrLZ5F3WbCxkzl67mmOnqik0zrg5W6D/X0QMW7HtvnoL1dRARLKIbMP3vXwkwziuLesPqWVGIl6Bw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.3.tgz", + "integrity": "sha512-5BirgNWNOx7cwbTJCOmKFJ1pZjwk5MUfMIwiBBvsirCJMZeQgs5pk6i1OlkVg+1Vef5LfBahFOrdCnAWvkVKMw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz", + "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/template": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", + "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.1.tgz", + "integrity": "sha512-rlhWtONnVBPdmt+jeewS0qSnMz/3yLFrqAP8hHC6EDcrYRSyuz9f9yQhHvVn2Ad6+yO9fHXac5piudeYrInxwQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "license": "MIT", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.3.tgz", + "integrity": "sha512-5Ti1cHLTDnt3vX61P9KZ5IG09bFXp4cDVFJIAeCZuxu9OXXJJZp5iP0n/rzM2+iAutJY+KWEyyHcRaHlpQ/P5g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz", + "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.3.tgz", + "integrity": "sha512-IuvOMdeOOY2X4hRNAT6kwbePtK21BUyrAEgLKviL8pL6AEEVUVcqtRdN/HJXBLGIbt9T3ETmXRnFedRRmQNTYw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.3.tgz", + "integrity": "sha512-CbayIfOw4av2v/HYZEsH+Klks3NC2/MFIR3QR8gnpGNNPEaq2fdlVCRYG/paKs7/5hvBLQ+H70pGWOHtlNEWNA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz", + "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.21.5", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-simple-access": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz", + "integrity": "sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-validator-identifier": "^7.19.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz", + "integrity": "sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz", + "integrity": "sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.3.tgz", + "integrity": "sha512-CpaoNp16nX7ROtLONNuCyenYdY/l7ZsR6aoVa7rW7nMWisoNoQNIH5Iay/4LDyRjKMuElMqXiBoOQCDLTMGZiw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.3.tgz", + "integrity": "sha512-+AF88fPDJrnseMh5vD9+SH6wq4ZMvpiTMHh58uLs+giMEyASFVhcT3NkoyO+NebFCNnpHJEq5AXO2txV4AGPDQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.3.tgz", + "integrity": "sha512-38bzTsqMMCI46/TQnJwPPpy33EjLCc1Gsm2hRTF6zTMWnKsN61vdrpuzIEGQyKEhDSYDKyZHrrd5FMj4gcUHhw==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.3", + "@babel/helper-compilation-targets": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.3.tgz", + "integrity": "sha512-bnDFWXFzWY0BsOyqaoSXvMQ2F35zutQipugog/rqotL2S4ciFOKlRYUu9djt4iq09oh2/34hqfRR2k1dIvuu4g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz", + "integrity": "sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz", + "integrity": "sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.3.tgz", + "integrity": "sha512-fC7jtjBPFqhqpPAE+O4LKwnLq7gGkD3ZmC2E3i4qWH34mH3gOg2Xrq5YMHUq6DM30xhqM1DNftiRaSqVjEG+ug==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.3.tgz", + "integrity": "sha512-C7MMl4qWLpgVCbXfj3UW8rR1xeCnisQ0cU7YJHV//8oNBS0aCIVg1vFnZXxOckHhEpQyqNNkWmvSEWnMLlc+Vw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.3.tgz", + "integrity": "sha512-b5J6muxQYp4H7loAQv/c7GO5cPuRA6H5hx4gO+/Hn+Cu9MRQU0PNiUoWq1L//8sq6kFSNxGXFb2XTaUfa9y+Pg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz", + "integrity": "sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/types": "^7.22.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz", + "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "regenerator-transform": "^0.15.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.4.tgz", + "integrity": "sha512-Urkiz1m4zqiRo17klj+l3nXgiRTFQng91Bc1eiLF7BMQu1e7wE5Gcq9xSv062IF068NHjcutSbIMev60gXxAvA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-plugin-utils": "^7.21.5", + "babel-plugin-polyfill-corejs2": "^0.4.3", + "babel-plugin-polyfill-corejs3": "^0.8.1", + "babel-plugin-polyfill-regenerator": "^0.5.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.3.tgz", + "integrity": "sha512-pyjnCIniO5PNaEuGxT28h0HbMru3qCVrMqVgVOz/krComdIrY9W6FCLBq9NWHY8HDGaUlan+UhmZElDENIfCcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/plugin-syntax-typescript": "^7.21.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz", + "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.3.tgz", + "integrity": "sha512-5ScJ+OmdX+O6HRuMGW4kv7RL9vIKdtdAj9wuWUKy1wbHY3jaM/UlyIiC1G7J6UJiiyMukjjK0QwL3P0vBd0yYg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.3.tgz", + "integrity": "sha512-hNufLdkF8vqywRp+P55j4FHXqAX2LRUccoZHH7AFn1pq5ZOO2ISKW9w13bFZVjBoTqeve2HOgoJCcaziJVhGNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.4.tgz", + "integrity": "sha512-c3lHOjbwBv0TkhYCr+XCR6wKcSZ1QbQTVdSkZUaVpLv8CVWotBMArWUi5UAJrcrQaEnleVkkvaV8F/pmc/STZQ==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.3", + "@babel/helper-compilation-targets": "^7.22.1", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.3", + "@babel/plugin-proposal-private-property-in-object": "^7.21.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-import-attributes": "^7.22.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.21.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.3", + "@babel/plugin-transform-async-to-generator": "^7.20.7", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.21.0", + "@babel/plugin-transform-class-properties": "^7.22.3", + "@babel/plugin-transform-class-static-block": "^7.22.3", + "@babel/plugin-transform-classes": "^7.21.0", + "@babel/plugin-transform-computed-properties": "^7.21.5", + "@babel/plugin-transform-destructuring": "^7.21.3", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-dynamic-import": "^7.22.1", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-export-namespace-from": "^7.22.3", + "@babel/plugin-transform-for-of": "^7.21.5", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-json-strings": "^7.22.3", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.3", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.20.11", + "@babel/plugin-transform-modules-commonjs": "^7.21.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.3", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.3", + "@babel/plugin-transform-new-target": "^7.22.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.3", + "@babel/plugin-transform-numeric-separator": "^7.22.3", + "@babel/plugin-transform-object-rest-spread": "^7.22.3", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-optional-catch-binding": "^7.22.3", + "@babel/plugin-transform-optional-chaining": "^7.22.3", + "@babel/plugin-transform-parameters": "^7.22.3", + "@babel/plugin-transform-private-methods": "^7.22.3", + "@babel/plugin-transform-private-property-in-object": "^7.22.3", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.21.5", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.20.7", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.21.5", + "@babel/plugin-transform-unicode-property-regex": "^7.22.3", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.3", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.22.4", + "babel-plugin-polyfill-corejs2": "^0.4.3", + "babel-plugin-polyfill-corejs3": "^0.8.1", + "babel-plugin-polyfill-regenerator": "^0.5.0", + "core-js-compat": "^3.30.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.3.tgz", + "integrity": "sha512-lxDz1mnZ9polqClBCVBjIVUypoB4qV3/tZUDb/IlYbW1kiiLaXaX+bInbRjl+lNQ/iUZraQ3+S8daEmoELMWug==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.22.3", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz", + "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.5", + "@babel/plugin-transform-typescript": "^7.21.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "license": "MIT" + }, + "node_modules/@babel/runtime": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.3.tgz", + "integrity": "sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.22.3.tgz", + "integrity": "sha512-6bdmknScYKmt8I9VjsJuKKGr+TwUb555FTf6tT1P/ANlCjTHCiYLhiQ4X/O7J731w5NOqu8c1aYHEVuOwPz7jA==", + "license": "MIT", + "dependencies": { + "core-js-pure": "^3.30.2", + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.21.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz", + "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/parser": "^7.21.9", + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz", + "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.3", + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.22.4", + "@babel/types": "^7.22.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", + "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.21.5", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.4.0.tgz", + "integrity": "sha512-Hg8Xfma+rFwRi6Y/pfei4FJoQ1hdVURmmNs/XPoMTCPAImU+d5yxj+M+qdLtNjWRpfWziU4dQcqY94xgFBn2dg==", + "license": "MIT" + }, + "node_modules/@docsearch/react": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.4.0.tgz", + "integrity": "sha512-ufrp5879XYGojgS30ZAp8H4qIMbahRHB9M85VDBP36Xgz5QjYM54i1URKj5e219F7gqTtOivfztFTij6itc0MQ==", + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.8.2", + "@algolia/autocomplete-preset-algolia": "1.8.2", + "@docsearch/css": "3.4.0", + "algoliasearch": "^4.0.0" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@docusaurus/core": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/core/-/core-2.4.3.tgz", + "integrity": "sha512-dWH5P7cgeNSIg9ufReX6gaCl/TmrGKD38Orbwuz05WPhAQtFXHd5B8Qym1TiXfvUNvwoYKkAJOJuGe8ou0Z7PA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.18.6", + "@babel/generator": "^7.18.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "^7.18.6", + "@babel/runtime-corejs3": "^7.18.6", + "@babel/traverse": "^7.18.8", + "@docusaurus/cssnano-preset": "2.4.3", + "@docusaurus/logger": "2.4.3", + "@docusaurus/mdx-loader": "2.4.3", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/utils": "2.4.3", + "@docusaurus/utils-common": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "@slorber/static-site-generator-webpack-plugin": "^4.0.7", + "@svgr/webpack": "^6.2.1", + "autoprefixer": "^10.4.7", + "babel-loader": "^8.2.5", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.0", + "cli-table3": "^0.6.2", + "combine-promises": "^1.1.0", + "commander": "^5.1.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.23.3", + "css-loader": "^6.7.1", + "css-minimizer-webpack-plugin": "^4.0.0", + "cssnano": "^5.1.12", + "del": "^6.1.1", + "detect-port": "^1.3.0", + "escape-html": "^1.0.3", + "eta": "^2.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^6.1.0", + "html-tags": "^3.2.0", + "html-webpack-plugin": "^5.5.0", + "import-fresh": "^3.3.0", + "leven": "^3.1.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.14", + "postcss-loader": "^7.0.0", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable-ssr-addon-v5-slorber": "^1.0.1", + "react-router": "^5.3.3", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.3", + "rtl-detect": "^1.0.4", + "semver": "^7.3.7", + "serve-handler": "^6.1.3", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.3", + "tslib": "^2.4.0", + "update-notifier": "^5.1.0", + "url-loader": "^4.1.1", + "wait-on": "^6.0.1", + "webpack": "^5.73.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-dev-server": "^4.9.3", + "webpack-merge": "^5.8.0", + "webpackbar": "^5.0.2" + }, + "bin": { + "docusaurus": "bin/docusaurus.mjs" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/cssnano-preset": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.3.tgz", + "integrity": "sha512-ZvGSRCi7z9wLnZrXNPG6DmVPHdKGd8dIn9pYbEOFiYihfv4uDR3UtxogmKf+rT8ZlKFf5Lqne8E8nt08zNM8CA==", + "license": "MIT", + "dependencies": { + "cssnano-preset-advanced": "^5.3.8", + "postcss": "^8.4.14", + "postcss-sort-media-queries": "^4.2.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/logger": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/logger/-/logger-2.4.3.tgz", + "integrity": "sha512-Zxws7r3yLufk9xM1zq9ged0YHs65mlRmtsobnFkdZTxWXdTYlWWLWdKyNKAsVC+D7zg+pv2fGbyabdOnyZOM3w==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/mdx-loader": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/mdx-loader/-/mdx-loader-2.4.3.tgz", + "integrity": "sha512-b1+fDnWtl3GiqkL0BRjYtc94FZrcDDBV1j8446+4tptB9BAOlePwG2p/pK6vGvfL53lkOsszXMghr2g67M0vCw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.18.8", + "@babel/traverse": "^7.18.8", + "@docusaurus/logger": "2.4.3", + "@docusaurus/utils": "2.4.3", + "@mdx-js/mdx": "^1.6.22", + "escape-html": "^1.0.3", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "image-size": "^1.0.1", + "mdast-util-to-string": "^2.0.0", + "remark-emoji": "^2.2.0", + "stringify-object": "^3.3.0", + "tslib": "^2.4.0", + "unified": "^9.2.2", + "unist-util-visit": "^2.0.3", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/module-type-aliases": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.3.tgz", + "integrity": "sha512-cwkBkt1UCiduuvEAo7XZY01dJfRn7UR/75mBgOdb1hKknhrabJZ8YH+7savd/y9kLExPyrhe0QwdS9GuzsRRIA==", + "license": "MIT", + "dependencies": { + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/types": "2.4.3", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@docusaurus/plugin-content-blog": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.3.tgz", + "integrity": "sha512-PVhypqaA0t98zVDpOeTqWUTvRqCEjJubtfFUQ7zJNYdbYTbS/E/ytq6zbLVsN/dImvemtO/5JQgjLxsh8XLo8Q==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/logger": "2.4.3", + "@docusaurus/mdx-loader": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils": "2.4.3", + "@docusaurus/utils-common": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "cheerio": "^1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "tslib": "^2.4.0", + "unist-util-visit": "^2.0.3", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-content-docs": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.3.tgz", + "integrity": "sha512-N7Po2LSH6UejQhzTCsvuX5NOzlC+HiXOVvofnEPj0WhMu1etpLEXE6a4aTxrtg95lQ5kf0xUIdjX9sh3d3G76A==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/logger": "2.4.3", + "@docusaurus/mdx-loader": "2.4.3", + "@docusaurus/module-type-aliases": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "@types/react-router-config": "^5.0.6", + "combine-promises": "^1.1.0", + "fs-extra": "^10.1.0", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-content-pages": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.3.tgz", + "integrity": "sha512-txtDVz7y3zGk67q0HjG0gRttVPodkHqE0bpJ+7dOaTH40CQFLSh7+aBeGnPOTl+oCPG+hxkim4SndqPqXjQ8Bg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/mdx-loader": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "fs-extra": "^10.1.0", + "tslib": "^2.4.0", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-debug": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/plugin-debug/-/plugin-debug-2.4.3.tgz", + "integrity": "sha512-LkUbuq3zCmINlFb+gAd4ZvYr+bPAzMC0hwND4F7V9bZ852dCX8YoWyovVUBKq4er1XsOwSQaHmNGtObtn8Av8Q==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils": "2.4.3", + "fs-extra": "^10.1.0", + "react-json-view": "^1.21.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-analytics": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.3.tgz", + "integrity": "sha512-KzBV3k8lDkWOhg/oYGxlK5o9bOwX7KpPc/FTWoB+SfKhlHfhq7qcQdMi1elAaVEIop8tgK6gD1E58Q+XC6otSQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-gtag": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.3.tgz", + "integrity": "sha512-5FMg0rT7sDy4i9AGsvJC71MQrqQZwgLNdDetLEGDHLfSHLvJhQbTCUGbGXknUgWXQJckcV/AILYeJy+HhxeIFA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-tag-manager": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.3.tgz", + "integrity": "sha512-1jTzp71yDGuQiX9Bi0pVp3alArV0LSnHXempvQTxwCGAEzUWWaBg4d8pocAlTpbP9aULQQqhgzrs8hgTRPOM0A==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-sitemap": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.3.tgz", + "integrity": "sha512-LRQYrK1oH1rNfr4YvWBmRzTL0LN9UAPxBbghgeFRBm5yloF6P+zv1tm2pe2hQTX/QP5bSKdnajCvfnScgKXMZQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/logger": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils": "2.4.3", + "@docusaurus/utils-common": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "fs-extra": "^10.1.0", + "sitemap": "^7.1.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/preset-classic": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/preset-classic/-/preset-classic-2.4.3.tgz", + "integrity": "sha512-tRyMliepY11Ym6hB1rAFSNGwQDpmszvWYJvlK1E+md4SW8i6ylNHtpZjaYFff9Mdk3i/Pg8ItQq9P0daOJAvQw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/plugin-content-blog": "2.4.3", + "@docusaurus/plugin-content-docs": "2.4.3", + "@docusaurus/plugin-content-pages": "2.4.3", + "@docusaurus/plugin-debug": "2.4.3", + "@docusaurus/plugin-google-analytics": "2.4.3", + "@docusaurus/plugin-google-gtag": "2.4.3", + "@docusaurus/plugin-google-tag-manager": "2.4.3", + "@docusaurus/plugin-sitemap": "2.4.3", + "@docusaurus/theme-classic": "2.4.3", + "@docusaurus/theme-common": "2.4.3", + "@docusaurus/theme-search-algolia": "2.4.3", + "@docusaurus/types": "2.4.3" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/react-loadable": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/@docusaurus/theme-classic": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/theme-classic/-/theme-classic-2.4.3.tgz", + "integrity": "sha512-QKRAJPSGPfDY2yCiPMIVyr+MqwZCIV2lxNzqbyUW0YkrlmdzzP3WuQJPMGLCjWgQp/5c9kpWMvMxjhpZx1R32Q==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "2.4.3", + "@docusaurus/mdx-loader": "2.4.3", + "@docusaurus/module-type-aliases": "2.4.3", + "@docusaurus/plugin-content-blog": "2.4.3", + "@docusaurus/plugin-content-docs": "2.4.3", + "@docusaurus/plugin-content-pages": "2.4.3", + "@docusaurus/theme-common": "2.4.3", + "@docusaurus/theme-translations": "2.4.3", + "@docusaurus/types": "2.4.3", + "@docusaurus/utils": "2.4.3", + "@docusaurus/utils-common": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.2.1", + "copy-text-to-clipboard": "^3.0.1", + "infima": "0.2.0-alpha.43", + "lodash": "^4.17.21", + "nprogress": "^0.2.0", + "postcss": "^8.4.14", + "prism-react-renderer": "^1.3.5", + "prismjs": "^1.28.0", + "react-router-dom": "^5.3.3", + "rtlcss": "^3.5.0", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/theme-common": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/theme-common/-/theme-common-2.4.3.tgz", + "integrity": "sha512-7KaDJBXKBVGXw5WOVt84FtN8czGWhM0lbyWEZXGp8AFfL6sZQfRTluFp4QriR97qwzSyOfQb+nzcDZZU4tezUw==", + "license": "MIT", + "dependencies": { + "@docusaurus/mdx-loader": "2.4.3", + "@docusaurus/module-type-aliases": "2.4.3", + "@docusaurus/plugin-content-blog": "2.4.3", + "@docusaurus/plugin-content-docs": "2.4.3", + "@docusaurus/plugin-content-pages": "2.4.3", + "@docusaurus/utils": "2.4.3", + "@docusaurus/utils-common": "2.4.3", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "clsx": "^1.2.1", + "parse-numeric-range": "^1.3.0", + "prism-react-renderer": "^1.3.5", + "tslib": "^2.4.0", + "use-sync-external-store": "^1.2.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/theme-search-algolia": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.3.tgz", + "integrity": "sha512-jziq4f6YVUB5hZOB85ELATwnxBz/RmSLD3ksGQOLDPKVzat4pmI8tddNWtriPpxR04BNT+ZfpPUMFkNFetSW1Q==", + "license": "MIT", + "dependencies": { + "@docsearch/react": "^3.1.1", + "@docusaurus/core": "2.4.3", + "@docusaurus/logger": "2.4.3", + "@docusaurus/plugin-content-docs": "2.4.3", + "@docusaurus/theme-common": "2.4.3", + "@docusaurus/theme-translations": "2.4.3", + "@docusaurus/utils": "2.4.3", + "@docusaurus/utils-validation": "2.4.3", + "algoliasearch": "^4.13.1", + "algoliasearch-helper": "^3.10.0", + "clsx": "^1.2.1", + "eta": "^2.0.0", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/theme-translations": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/theme-translations/-/theme-translations-2.4.3.tgz", + "integrity": "sha512-H4D+lbZbjbKNS/Zw1Lel64PioUAIT3cLYYJLUf3KkuO/oc9e0QCVhIYVtUI2SfBCF2NNdlyhBDQEEMygsCedIg==", + "license": "MIT", + "dependencies": { + "fs-extra": "^10.1.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/types": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/types/-/types-2.4.3.tgz", + "integrity": "sha512-W6zNLGQqfrp/EoPD0bhb9n7OobP+RHpmvVzpA+Z/IuU3Q63njJM24hmT0GYboovWcDtFmnIJC9wcyx4RVPQscw==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.6.0", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0", + "webpack-merge": "^5.8.0" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/utils": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/utils/-/utils-2.4.3.tgz", + "integrity": "sha512-fKcXsjrD86Smxv8Pt0TBFqYieZZCPh4cbf9oszUq/AMhZn3ujwpKaVYZACPX8mmjtYx0JOgNx52CREBfiGQB4A==", + "license": "MIT", + "dependencies": { + "@docusaurus/logger": "2.4.3", + "@svgr/webpack": "^6.2.1", + "escape-string-regexp": "^4.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "github-slugger": "^1.4.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", + "resolve-pathname": "^3.0.0", + "shelljs": "^0.8.5", + "tslib": "^2.4.0", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } + } + }, + "node_modules/@docusaurus/utils-common": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/utils-common/-/utils-common-2.4.3.tgz", + "integrity": "sha512-/jascp4GbLQCPVmcGkPzEQjNaAk3ADVfMtudk49Ggb+131B1WDD6HqlSmDf8MxGdy7Dja2gc+StHf01kiWoTDQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } + } + }, + "node_modules/@docusaurus/utils-validation": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/@docusaurus/utils-validation/-/utils-validation-2.4.3.tgz", + "integrity": "sha512-G2+Vt3WR5E/9drAobP+hhZQMaswRwDlp6qOMi7o7ZypB+VO7N//DZWhZEwhcRGepMDJGQEwtPv7UxtYwPL9PBw==", + "license": "MIT", + "dependencies": { + "@docusaurus/logger": "2.4.3", + "@docusaurus/utils": "2.4.3", + "joi": "^17.6.0", + "js-yaml": "^4.1.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "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==", + "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==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@headlessui/react": { + "version": "1.7.17", + "resolved": "https://registry.npmmirror.com/@headlessui/react/-/react-1.7.17.tgz", + "integrity": "sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==", + "license": "MIT", + "dependencies": { + "client-only": "^0.0.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "license": "MIT" + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "license": "MIT" + }, + "node_modules/@mdx-js/mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", + "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", + "license": "MIT", + "dependencies": { + "@babel/core": "7.12.9", + "@babel/plugin-syntax-jsx": "7.12.1", + "@babel/plugin-syntax-object-rest-spread": "7.8.3", + "@mdx-js/util": "1.6.22", + "babel-plugin-apply-mdx-type-prop": "1.6.22", + "babel-plugin-extract-import-names": "1.6.22", + "camelcase-css": "2.0.1", + "detab": "2.0.4", + "hast-util-raw": "6.0.1", + "lodash.uniq": "4.5.0", + "mdast-util-to-hast": "10.0.1", + "remark-footnotes": "2.0.0", + "remark-mdx": "1.6.22", + "remark-parse": "8.0.3", + "remark-squeeze-paragraphs": "4.0.0", + "style-to-object": "0.3.0", + "unified": "9.2.0", + "unist-builder": "2.0.3", + "unist-util-visit": "2.0.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@mdx-js/mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@mdx-js/mdx/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@mdx-js/mdx/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@mdx-js/mdx/node_modules/unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "license": "MIT", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/react": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", + "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" + } + }, + "node_modules/@mdx-js/util": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", + "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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==", + "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==", + "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==", + "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.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "license": "MIT" + }, + "node_modules/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "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==", + "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==", + "license": "BSD-3-Clause" + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@slorber/static-site-generator-webpack-plugin": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", + "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", + "license": "MIT", + "dependencies": { + "eval": "^0.1.8", + "p-map": "^4.0.0", + "webpack-sources": "^3.2.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", + "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", + "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", + "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", + "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", + "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", + "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", + "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", + "@svgr/babel-plugin-remove-jsx-attribute": "*", + "@svgr/babel-plugin-remove-jsx-empty-expression": "*", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", + "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", + "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", + "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", + "@svgr/babel-plugin-transform-svg-component": "^6.5.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", + "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", + "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.0", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", + "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/hast-util-to-babel-ast": "^6.5.1", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "^6.0.0" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", + "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "svgo": "^2.8.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/webpack": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", + "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.19.6", + "@babel/plugin-transform-react-constant-elements": "^7.18.12", + "@babel/preset-env": "^7.19.4", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@svgr/core": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "@svgr/plugin-svgo": "^6.5.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "ISC", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@tsconfig/docusaurus": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@tsconfig/docusaurus/-/docusaurus-1.0.7.tgz", + "integrity": "sha512-ffTXxGIP/IRMCjuzHd6M4/HdIrw1bMfC7Bv8hMkTadnePkpe0lG0oDSdbRpSDZb2rQMAgpbWiR10BvxvNYwYrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.0.tgz", + "integrity": "sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g==", + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "license": "MIT", + "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": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.11", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", + "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.2.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", + "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", + "license": "MIT" + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "license": "MIT" + }, + "node_modules/@types/parse5": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", + "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.7.tgz", + "integrity": "sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-config": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.7.tgz", + "integrity": "sha512-pFFVXUIydHlcJP6wJm7sDii5mD/bCmmAY0wQzq+M+uX7bqS95AQqHZWP1iNMKrWVQSuHIzj5qi9BvrtLX2/T4w==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "^5.1.0" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/@types/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "license": "MIT", + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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==", + "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.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.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==", + "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==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/algoliasearch": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.17.1.tgz", + "integrity": "sha512-4GDQ1RhP2qUR3x8PevFRbEdqZqIARNViZYjgTJmA1T7wRNtFA3W4Aqc/RsODqa1J8IO/QDla5x4tWuUS8NV8wA==", + "license": "MIT", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.17.1", + "@algolia/cache-common": "4.17.1", + "@algolia/cache-in-memory": "4.17.1", + "@algolia/client-account": "4.17.1", + "@algolia/client-analytics": "4.17.1", + "@algolia/client-common": "4.17.1", + "@algolia/client-personalization": "4.17.1", + "@algolia/client-search": "4.17.1", + "@algolia/logger-common": "4.17.1", + "@algolia/logger-console": "4.17.1", + "@algolia/requester-browser-xhr": "4.17.1", + "@algolia/requester-common": "4.17.1", + "@algolia/requester-node-http": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/algoliasearch-helper": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.13.0.tgz", + "integrity": "sha512-kV3c1jMQCvkARtGsSDvAwuht4PAMSsQILqPiH4WFiARoa3jXJ/r1TQoBWAjWyWF48rsNYCv7kzxgB4LTxrvvuw==", + "license": "MIT", + "dependencies": { + "@algolia/events": "^4.0.1" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 6" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/client-common": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.1.tgz", + "integrity": "sha512-+r7kg4EgbFnGsDnoGSVNtXZO8xvZ0vzf1WAOV7sqV9PMf1bp6cpJP/3IuPrSk4t5w2KVl+pC8jfTM7HcFlfBEQ==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/client-search": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.1.tgz", + "integrity": "sha512-Q5YfT5gVkx60PZDQBqp/zH9aUbBdC7HVvxupiHUgnCKqRQsRZjOhLest7AI6FahepuZLBZS62COrO7v+JvKY7w==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.17.1", + "@algolia/requester-common": "4.17.1", + "@algolia/transporter": "4.17.1" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/requester-browser-xhr": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.17.1.tgz", + "integrity": "sha512-W5mGfGDsyfVR+r4pUFrYLGBEM18gs38+GNt5PE5uPULy4uVTSnnVSkJkWeRkmLBk9zEZ/Nld8m4zavK6dtEuYg==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.17.1" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/requester-node-http": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.17.1.tgz", + "integrity": "sha512-NzFWecXT6d0PPsQY9L+/qoK2deF74OLcpvqCH+Vh3mh+QzPsFafcBExdguAjZsAWDn1R6JEeFW7/fo/p0SE57w==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.17.1" + } + }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==", + "license": "MIT" + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "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==", + "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==", + "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.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "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==", + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.16", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.7" + } + }, + "node_modules/babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "license": "MIT", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "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-plugin-apply-mdx-type-prop": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", + "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.10.4", + "@mdx-js/util": "1.6.22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@babel/core": "^7.11.6" + } + }, + "node_modules/babel-plugin-apply-mdx-type-prop/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "license": "MIT" + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "license": "MIT", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-extract-import-names": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", + "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.10.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/babel-plugin-extract-import-names/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "license": "MIT" + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz", + "integrity": "sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.4.0", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz", + "integrity": "sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.0", + "core-js-compat": "^3.30.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz", + "integrity": "sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-prismjs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-prismjs/-/babel-plugin-prismjs-2.1.0.tgz", + "integrity": "sha512-ehzSKYfeAz4U78zi/sfwsjDPlq0LvDKxNefcZTJ/iKBu+plsHsLqZhUeGf1+82LAcA35UZGbU6ksEx2Utphc/g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prismjs": "^1.18.0" + } + }, + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "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.11.0", + "raw-body": "2.5.1", + "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/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "license": "MIT", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "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==", + "license": "ISC" + }, + "node_modules/boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "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.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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==", + "license": "MIT", + "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.30001558", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz", + "integrity": "sha512-/Et7DwLqpjS47JPEcz6VnxU9PwcIdVi0ciLXRWBQdj1XFye68pSQYpV0QtPTfUKWuOaEig+/Vez2l74eDc1tPQ==", + "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/ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "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" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "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==", + "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==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "node_modules/combine-promises": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz", + "integrity": "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compressible/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/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==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "license": "BSD-2-Clause", + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/copy-text-to-clipboard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz", + "integrity": "sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-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==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/copy-webpack-plugin/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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", + "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", + "license": "MIT", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-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==", + "license": "MIT" + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", + "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "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": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-js": { + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.2.tgz", + "integrity": "sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", + "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.30.2.tgz", + "integrity": "sha512-p/npFUJXXBkCCTIlEGBdghofn00jWG6ZOtdoIXSJmAu2QBvN0IqpZXWweOytcwE6cfx8ZvVUy1vw8zxhe4Y2vg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "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/cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.11" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", + "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", + "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.21", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.3", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "license": "MIT", + "dependencies": { + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", + "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "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": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "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==", + "license": "MIT", + "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==", + "license": "BSD-2-Clause", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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-advanced": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz", + "integrity": "sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==", + "license": "MIT", + "dependencies": { + "autoprefixer": "^10.4.12", + "cssnano-preset-default": "^5.2.14", + "postcss-discard-unused": "^5.1.0", + "postcss-merge-idents": "^5.1.1", + "postcss-reduce-idents": "^5.2.0", + "postcss-zindex": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/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==", + "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==", + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "license": "MIT", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "license": "MIT", + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detab": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz", + "integrity": "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==", + "license": "MIT", + "dependencies": { + "repeat-string": "^1.5.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "license": "MIT", + "dependencies": { + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + } + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "license": "MIT", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "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==", + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", + "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/docusaurus-plugin-sass": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/docusaurus-plugin-sass/-/docusaurus-plugin-sass-0.2.3.tgz", + "integrity": "sha512-FbaE06K8NF8SPUYTwiG+83/jkXrwHJ/Afjqz3SUIGon6QvFwSSoKOcoxGQmUBnjTOk+deUONDx8jNWsegFJcBQ==", + "license": "MIT", + "dependencies": { + "sass-loader": "^10.1.1" + }, + "peerDependencies": { + "@docusaurus/core": "^2.0.0-beta", + "sass": "^1.30.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.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==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "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==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop/node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "license": "BSD-3-Clause" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.571", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.571.tgz", + "integrity": "sha512-Sc+VtKwKCDj3f/kLBjdyjMpNzoZsU6WuL/wFb6EH8USmHEcebxRXcRrVpOpayxd52tuey4RUDpUsw5OS5LhJqg==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/emoticon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-3.2.0.tgz", + "integrity": "sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", + "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/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==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", + "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "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==", + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eta": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", + "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eval": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "dependencies": { + "@types/node": "*", + "require-like": ">= 0.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "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" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/express/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "license": "MIT" + }, + "node_modules/express/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-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==", + "license": "MIT" + }, + "node_modules/fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", + "license": "MIT", + "dependencies": { + "punycode": "^1.3.2" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "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==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "license": "BSD-3-Clause", + "dependencies": { + "fbjs": "^3.0.0" + } + }, + "node_modules/fbjs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", + "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", + "license": "MIT", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.30" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "license": "MIT" + }, + "node_modules/feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "license": "MIT", + "dependencies": { + "xml-js": "^1.6.11" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", + "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "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/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "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==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-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==", + "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==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flux": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", + "license": "BSD-3-Clause", + "dependencies": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + }, + "peerDependencies": { + "react": "^15.0.2 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "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==", + "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/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "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/fork-ts-checker-webpack-plugin/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==", + "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/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==", + "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/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "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==", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", + "license": "ISC" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "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": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "license": "BSD-2-Clause" + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "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==", + "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==", + "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/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hast-to-hyperscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", + "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.3", + "comma-separated-tokens": "^1.0.0", + "property-information": "^5.3.0", + "space-separated-tokens": "^1.0.0", + "style-to-object": "^0.3.0", + "unist-util-is": "^4.0.0", + "web-namespaces": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz", + "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==", + "license": "MIT", + "dependencies": { + "@types/parse5": "^5.0.0", + "hastscript": "^6.0.0", + "property-information": "^5.0.0", + "vfile": "^4.0.0", + "vfile-location": "^3.2.0", + "web-namespaces": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz", + "integrity": "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-from-parse5": "^6.0.0", + "hast-util-to-parse5": "^6.0.0", + "html-void-elements": "^1.0.0", + "parse5": "^6.0.0", + "unist-util-position": "^3.0.0", + "vfile": "^4.0.0", + "web-namespaces": "^1.0.0", + "xtend": "^4.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT" + }, + "node_modules/hast-util-to-parse5": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", + "integrity": "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==", + "license": "MIT", + "dependencies": { + "hast-to-hyperscript": "^9.0.0", + "property-information": "^5.0.0", + "web-namespaces": "^1.0.0", + "xtend": "^4.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/html-void-elements": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", + "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz", + "integrity": "sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==", + "license": "MIT", + "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": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "license": "BSD-2-Clause" + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", + "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", + "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infima": { + "version": "0.2.0-alpha.43", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", + "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "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==", + "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==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "license": "MIT" + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "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", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "license": "MIT", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "license": "MIT", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", + "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-word-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", + "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.5.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/joi": { + "version": "17.9.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz", + "integrity": "sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.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==", + "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": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", + "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==", + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "license": "MIT", + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/launch-editor": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", + "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "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==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "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==", + "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==", + "license": "MIT" + }, + "node_modules/lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/markdown-escapes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", + "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==", + "license": "MIT", + "dependencies": { + "unist-util-remove": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "license": "MIT", + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", + "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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==", + "license": "CC0-1.0" + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.1.tgz", + "integrity": "sha512-UWbFJKvj5k+nETdteFndTpYxdeTMox/ULeqX5k/dpaQJCCFmj5EeKv3dBcyO2xmkRAx2vppRu5dVG7SOtsGOzA==", + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "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==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", + "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "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.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", + "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "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": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "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==", + "license": "MIT", + "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.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "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.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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==", + "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==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "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==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", + "license": "MIT" + }, + "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==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "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==", + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.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==", + "license": "MIT", + "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==", + "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==", + "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==", + "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==", + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "license": "MIT", + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "license": "MIT", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "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/parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", + "license": "ISC" + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "license": "MIT", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "license": "MIT", + "dependencies": { + "isarray": "0.0.1" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-unused": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", + "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "license": "MIT", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", + "license": "ISC", + "engines": { + "node": ">= 14" + } + }, + "node_modules/postcss-loader": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.2.tgz", + "integrity": "sha512-c7qDlXErX6n0VT+LUsW+nwefVtTu3ORtVvK8EXuUIDcxo+b/euYqpuHlJAvePb0Af5e8uMjR/13e0lTuYifaig==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^8.1.3", + "jiti": "^1.18.2", + "klona": "^2.0.6", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 14.15.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": "8.1.3", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", + "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", + "license": "MIT", + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + } + }, + "node_modules/postcss-merge-idents": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", + "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", + "license": "MIT", + "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-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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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-idents": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", + "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", + "license": "MIT", + "dependencies": { + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-sort-media-queries": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", + "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", + "license": "MIT", + "dependencies": { + "sort-css-media-queries": "2.1.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.16" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/postcss-zindex": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", + "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prism-react-renderer": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", + "integrity": "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==", + "license": "MIT", + "peerDependencies": { + "react": ">=0.14.9" + } + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-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==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "license": "MIT", + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==", + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "license": "MIT", + "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/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "license": "MIT", + "dependencies": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/react-dev-utils/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", + "license": "MIT" + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, + "node_modules/react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "license": "MIT", + "dependencies": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^16.3.0 || ^15.5.4", + "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" + } + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, + "node_modules/react-loadable": { + "name": "@docusaurus/react-loadable", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-loadable-ssr-addon-v5-slorber": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", + "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.3" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "react-loadable": "*", + "webpack": ">=4.41.1 || 5.x" + } + }, + "node_modules/react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-config": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", + "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.1.2" + }, + "peerDependencies": { + "react": ">=15", + "react-router": ">=5" + } + }, + "node_modules/react-router-dom": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz", + "integrity": "sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==", + "license": "MIT" + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "license": "MIT", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "license": "MIT", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "license": "MIT", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remark-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz", + "integrity": "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==", + "license": "MIT", + "dependencies": { + "emoticon": "^3.2.0", + "node-emoji": "^1.10.0", + "unist-util-visit": "^2.0.3" + } + }, + "node_modules/remark-footnotes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", + "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", + "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", + "license": "MIT", + "dependencies": { + "@babel/core": "7.12.9", + "@babel/helper-plugin-utils": "7.10.4", + "@babel/plugin-proposal-object-rest-spread": "7.12.1", + "@babel/plugin-syntax-jsx": "7.12.1", + "@mdx-js/util": "1.6.22", + "is-alphabetical": "1.0.4", + "remark-parse": "8.0.3", + "unified": "9.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx/node_modules/@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/remark-mdx/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "license": "MIT" + }, + "node_modules/remark-mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/remark-mdx/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/remark-mdx/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remark-mdx/node_modules/unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "license": "MIT", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "license": "MIT", + "dependencies": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", + "license": "MIT", + "dependencies": { + "mdast-squeeze-paragraphs": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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==", + "license": "MIT", + "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/renderkid/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==", + "license": "BSD-2-Clause", + "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/renderkid/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==", + "license": "MIT", + "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/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "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/renderkid/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", + "engines": { + "node": "*" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", + "license": "MIT" + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "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==", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rtl-detect": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz", + "integrity": "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==", + "license": "BSD-3-Clause" + }, + "node_modules/rtlcss": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz", + "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.3.11", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + } + }, + "node_modules/rtlcss/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rtlcss/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rtlcss/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rtlcss/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.1.tgz", + "integrity": "sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==", + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.4.1.tgz", + "integrity": "sha512-aX/iJZTTpNUNx/OSYzo2KsjIUQHqvWsAhhUijFjAPdZTEhstjZI9zTNvkTTwsx+uNUJqUwOw5gacxQMx4hJxGQ==", + "license": "MIT", + "dependencies": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sass-loader/node_modules/schema-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", + "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "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/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "license": "ISC" + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "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==", + "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/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "license": "MIT", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "license": "MIT", + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver/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==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/send/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-handler": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", + "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "fast-url-parser": "1.1.3", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "2.2.1", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/path-to-regexp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", + "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "ISC" + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "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==", + "license": "ISC" + }, + "node_modules/sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", + "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", + "license": "MIT", + "dependencies": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.2.4" + }, + "bin": { + "sitemap": "dist/cli.js" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=5.6.0" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "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==", + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sort-css-media-queries": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", + "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==", + "license": "MIT", + "engines": { + "node": ">= 6.3.0" + } + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "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==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "license": "MIT", + "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==", + "license": "MIT", + "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/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "license": "MIT" + }, + "node_modules/state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", + "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-object": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", + "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgo/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==", + "license": "BSD-2-Clause", + "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/svgo/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==", + "license": "MIT", + "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/svgo/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "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/svgo/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/tailwindcss": { + "version": "3.3.5", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.3.5.tgz", + "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.17.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.6.tgz", + "integrity": "sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "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.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "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/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==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", + "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "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/terser-webpack-plugin/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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", + "license": "MIT" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==" + }, + "node_modules/trim-trailing-lines": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", + "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz", + "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz", + "integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "license": "MIT", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unist-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-generated": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz", + "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==", + "license": "MIT", + "dependencies": { + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "license": "MIT", + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "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.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/update-notifier/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier/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==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/url-loader/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", + "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "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/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", + "license": "MIT", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "license": "MIT", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.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==", + "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==", + "license": "MIT" + }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", + "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/wait-on": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz", + "integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==", + "license": "MIT", + "dependencies": { + "axios": "^0.25.0", + "joi": "^17.6.0", + "lodash": "^4.17.21", + "minimist": "^1.2.5", + "rxjs": "^7.5.4" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "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==", + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-namespaces": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", + "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.84.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.84.1.tgz", + "integrity": "sha512-ZP4qaZ7vVn/K8WN/p990SGATmrL1qg4heP/MrVneczYtpDGJWlrgZv55vxaV2ul885Kz+25MP2kSXkPe3LZfmg==", + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.14.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.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "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.8.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz", + "integrity": "sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==", + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "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==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "license": "MIT", + "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.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/webpack-dev-middleware/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", + "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "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": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz", + "integrity": "sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ==", + "license": "MIT", + "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.1", + "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.1", + "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.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", + "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "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": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", + "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "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==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", + "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "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/webpackbar": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", + "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.3", + "pretty-time": "^1.1.0", + "std-env": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "webpack": "3 || 4 || 5" + } + }, + "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==", + "license": "Apache-2.0", + "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==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "license": "MIT" + }, + "node_modules/wowjs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wowjs/-/wowjs-1.1.3.tgz", + "integrity": "sha512-HQp1gi56wYmjOYYOMZ08TnDGpT+AO21RJVa0t1NJ3jU8l3dMyP+sY7TO/lilzVp4JFjW88bBY87RnpxdpSKofA==", + "dependencies": { + "animate.css": "latest" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "license": "MIT", + "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/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "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==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/website/vercel.json b/docs/website/vercel.json new file mode 100644 index 00000000000..0190921d038 --- /dev/null +++ b/docs/website/vercel.json @@ -0,0 +1,24 @@ +{ + "redirects": [ + { + "source": "/zh-Hans/blog/:path(.*)", + "destination": "https://blog.sealos.run/blog/:path", + "permanent": true + }, + { + "source": "/zh-Hans/blog", + "destination": "https://blog.sealos.run/blog", + "permanent": true + }, + { + "source": "/zh-Hans/:path(.*)", + "destination": "https://sealos.run/:path", + "permanent": true + }, + { + "source": "/zh-Hans", + "destination": "https://sealos.run/", + "permanent": true + } + ] + } \ No newline at end of file diff --git a/docs/website/yarn.lock b/docs/website/yarn.lock index 0d5f203176f..b12fbda18eb 100644 --- a/docs/website/yarn.lock +++ b/docs/website/yarn.lock @@ -67,6 +67,9 @@ "@algolia/requester-common" "4.17.1" "@algolia/transporter" "4.17.1" +"@algolia/client-common@5.18.0": + version "5.18.0" + "@algolia/client-personalization@4.17.1": version "4.17.1" resolved "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.17.1.tgz" @@ -76,6 +79,14 @@ "@algolia/requester-common" "4.17.1" "@algolia/transporter" "4.17.1" +"@algolia/client-search@>= 4.9.1 < 6": + version "5.18.0" + dependencies: + "@algolia/client-common" "5.18.0" + "@algolia/requester-browser-xhr" "5.18.0" + "@algolia/requester-fetch" "5.18.0" + "@algolia/requester-node-http" "5.18.0" + "@algolia/client-search@4.17.1": version "4.17.1" resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.1.tgz" @@ -109,11 +120,21 @@ dependencies: "@algolia/requester-common" "4.17.1" +"@algolia/requester-browser-xhr@5.18.0": + version "5.18.0" + dependencies: + "@algolia/client-common" "5.18.0" + "@algolia/requester-common@4.17.1": version "4.17.1" resolved "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.17.1.tgz" integrity sha512-HggXdjvVFQR0I5l7hM5WdHgQ1tqcRWeyXZz8apQ7zPWZhirmY2E9D6LVhDh/UnWQNEm7nBtM+eMFONJ3bZccIQ== +"@algolia/requester-fetch@5.18.0": + version "5.18.0" + dependencies: + "@algolia/client-common" "5.18.0" + "@algolia/requester-node-http@4.17.1": version "4.17.1" resolved "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.17.1.tgz" @@ -121,6 +142,11 @@ dependencies: "@algolia/requester-common" "4.17.1" +"@algolia/requester-node-http@5.18.0": + version "5.18.0" + dependencies: + "@algolia/client-common" "5.18.0" + "@algolia/transporter@4.17.1": version "4.17.1" resolved "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.17.1.tgz" @@ -132,7 +158,7 @@ "@alloc/quick-lru@^5.2.0": version "5.2.0" - resolved "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + resolved "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== "@ampproject/remapping@^2.2.0": @@ -155,6 +181,27 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz" integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.18.6", "@babel/core@^7.19.6", "@babel/core@^7.4.0-0": + version "7.22.1" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz" + integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.22.0" + "@babel/helper-compilation-targets" "^7.22.1" + "@babel/helper-module-transforms" "^7.22.1" + "@babel/helpers" "^7.22.0" + "@babel/parser" "^7.22.0" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + "@babel/core@7.12.9": version "7.12.9" resolved "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz" @@ -177,27 +224,6 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.18.6", "@babel/core@^7.19.6": - version "7.22.1" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz" - integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.22.0" - "@babel/helper-compilation-targets" "^7.22.1" - "@babel/helper-module-transforms" "^7.22.1" - "@babel/helpers" "^7.22.0" - "@babel/parser" "^7.22.0" - "@babel/template" "^7.21.9" - "@babel/traverse" "^7.22.1" - "@babel/types" "^7.22.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - "@babel/generator@^7.12.5", "@babel/generator@^7.18.7", "@babel/generator@^7.22.0", "@babel/generator@^7.22.3": version "7.22.3" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz" @@ -324,16 +350,16 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@7.10.4": - version "7.10.4" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz" - integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== - "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.21.5" resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz" integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== +"@babel/helper-plugin-utils@7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz" @@ -531,13 +557,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@7.12.1": - version "7.12.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz" - integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx@^7.21.4": version "7.21.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz" @@ -545,6 +564,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-syntax-jsx@7.12.1": + version "7.12.1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" @@ -566,7 +592,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3", "@babel/plugin-syntax-object-rest-spread@7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== @@ -1265,9 +1291,9 @@ "@docsearch/css" "3.4.0" algoliasearch "^4.0.0" -"@docusaurus/core@2.4.3", "@docusaurus/core@^2.4.3": +"@docusaurus/core@^2.0.0-beta", "@docusaurus/core@^2.4.3", "@docusaurus/core@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/core/-/core-2.4.3.tgz#d86624901386fd8164ce4bff9cc7f16fde57f523" + resolved "https://registry.npmmirror.com/@docusaurus/core/-/core-2.4.3.tgz" integrity sha512-dWH5P7cgeNSIg9ufReX6gaCl/TmrGKD38Orbwuz05WPhAQtFXHd5B8Qym1TiXfvUNvwoYKkAJOJuGe8ou0Z7PA== dependencies: "@babel/core" "^7.18.6" @@ -1344,7 +1370,7 @@ "@docusaurus/cssnano-preset@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.3.tgz#1d7e833c41ce240fcc2812a2ac27f7b862f32de0" + resolved "https://registry.npmmirror.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.3.tgz" integrity sha512-ZvGSRCi7z9wLnZrXNPG6DmVPHdKGd8dIn9pYbEOFiYihfv4uDR3UtxogmKf+rT8ZlKFf5Lqne8E8nt08zNM8CA== dependencies: cssnano-preset-advanced "^5.3.8" @@ -1354,7 +1380,7 @@ "@docusaurus/logger@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/logger/-/logger-2.4.3.tgz#518bbc965fb4ebe8f1d0b14e5f4161607552d34c" + resolved "https://registry.npmmirror.com/@docusaurus/logger/-/logger-2.4.3.tgz" integrity sha512-Zxws7r3yLufk9xM1zq9ged0YHs65mlRmtsobnFkdZTxWXdTYlWWLWdKyNKAsVC+D7zg+pv2fGbyabdOnyZOM3w== dependencies: chalk "^4.1.2" @@ -1362,7 +1388,7 @@ "@docusaurus/mdx-loader@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/mdx-loader/-/mdx-loader-2.4.3.tgz#e8ff37f30a060eaa97b8121c135f74cb531a4a3e" + resolved "https://registry.npmmirror.com/@docusaurus/mdx-loader/-/mdx-loader-2.4.3.tgz" integrity sha512-b1+fDnWtl3GiqkL0BRjYtc94FZrcDDBV1j8446+4tptB9BAOlePwG2p/pK6vGvfL53lkOsszXMghr2g67M0vCw== dependencies: "@babel/parser" "^7.18.8" @@ -1383,9 +1409,9 @@ url-loader "^4.1.1" webpack "^5.73.0" -"@docusaurus/module-type-aliases@2.4.3", "@docusaurus/module-type-aliases@^2.4.3": +"@docusaurus/module-type-aliases@^2.4.3", "@docusaurus/module-type-aliases@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.3.tgz#d08ef67e4151e02f352a2836bcf9ecde3b9c56ac" + resolved "https://registry.npmmirror.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.3.tgz" integrity sha512-cwkBkt1UCiduuvEAo7XZY01dJfRn7UR/75mBgOdb1hKknhrabJZ8YH+7savd/y9kLExPyrhe0QwdS9GuzsRRIA== dependencies: "@docusaurus/react-loadable" "5.5.2" @@ -1399,7 +1425,7 @@ "@docusaurus/plugin-content-blog@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.3.tgz#6473b974acab98e967414d8bbb0d37e0cedcea14" + resolved "https://registry.npmmirror.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.3.tgz" integrity sha512-PVhypqaA0t98zVDpOeTqWUTvRqCEjJubtfFUQ7zJNYdbYTbS/E/ytq6zbLVsN/dImvemtO/5JQgjLxsh8XLo8Q== dependencies: "@docusaurus/core" "2.4.3" @@ -1421,7 +1447,7 @@ "@docusaurus/plugin-content-docs@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.3.tgz#aa224c0512351e81807adf778ca59fd9cd136973" + resolved "https://registry.npmmirror.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.3.tgz" integrity sha512-N7Po2LSH6UejQhzTCsvuX5NOzlC+HiXOVvofnEPj0WhMu1etpLEXE6a4aTxrtg95lQ5kf0xUIdjX9sh3d3G76A== dependencies: "@docusaurus/core" "2.4.3" @@ -1443,7 +1469,7 @@ "@docusaurus/plugin-content-pages@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.3.tgz#7f285e718b53da8c8d0101e70840c75b9c0a1ac0" + resolved "https://registry.npmmirror.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.3.tgz" integrity sha512-txtDVz7y3zGk67q0HjG0gRttVPodkHqE0bpJ+7dOaTH40CQFLSh7+aBeGnPOTl+oCPG+hxkim4SndqPqXjQ8Bg== dependencies: "@docusaurus/core" "2.4.3" @@ -1457,7 +1483,7 @@ "@docusaurus/plugin-debug@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/plugin-debug/-/plugin-debug-2.4.3.tgz#2f90eb0c9286a9f225444e3a88315676fe02c245" + resolved "https://registry.npmmirror.com/@docusaurus/plugin-debug/-/plugin-debug-2.4.3.tgz" integrity sha512-LkUbuq3zCmINlFb+gAd4ZvYr+bPAzMC0hwND4F7V9bZ852dCX8YoWyovVUBKq4er1XsOwSQaHmNGtObtn8Av8Q== dependencies: "@docusaurus/core" "2.4.3" @@ -1469,7 +1495,7 @@ "@docusaurus/plugin-google-analytics@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.3.tgz#0d19993136ade6f7a7741251b4f617400d92ab45" + resolved "https://registry.npmmirror.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.3.tgz" integrity sha512-KzBV3k8lDkWOhg/oYGxlK5o9bOwX7KpPc/FTWoB+SfKhlHfhq7qcQdMi1elAaVEIop8tgK6gD1E58Q+XC6otSQ== dependencies: "@docusaurus/core" "2.4.3" @@ -1479,7 +1505,7 @@ "@docusaurus/plugin-google-gtag@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.3.tgz#e1a80b0696771b488562e5b60eff21c9932d9e1c" + resolved "https://registry.npmmirror.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.3.tgz" integrity sha512-5FMg0rT7sDy4i9AGsvJC71MQrqQZwgLNdDetLEGDHLfSHLvJhQbTCUGbGXknUgWXQJckcV/AILYeJy+HhxeIFA== dependencies: "@docusaurus/core" "2.4.3" @@ -1489,7 +1515,7 @@ "@docusaurus/plugin-google-tag-manager@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.3.tgz#e41fbf79b0ffc2de1cc4013eb77798cff0ad98e3" + resolved "https://registry.npmmirror.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.3.tgz" integrity sha512-1jTzp71yDGuQiX9Bi0pVp3alArV0LSnHXempvQTxwCGAEzUWWaBg4d8pocAlTpbP9aULQQqhgzrs8hgTRPOM0A== dependencies: "@docusaurus/core" "2.4.3" @@ -1499,7 +1525,7 @@ "@docusaurus/plugin-sitemap@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.3.tgz#1b3930900a8f89670ce7e8f83fb4730cd3298c32" + resolved "https://registry.npmmirror.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.3.tgz" integrity sha512-LRQYrK1oH1rNfr4YvWBmRzTL0LN9UAPxBbghgeFRBm5yloF6P+zv1tm2pe2hQTX/QP5bSKdnajCvfnScgKXMZQ== dependencies: "@docusaurus/core" "2.4.3" @@ -1514,7 +1540,7 @@ "@docusaurus/preset-classic@^2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/preset-classic/-/preset-classic-2.4.3.tgz#074c57ebf29fa43d23bd1c8ce691226f542bc262" + resolved "https://registry.npmmirror.com/@docusaurus/preset-classic/-/preset-classic-2.4.3.tgz" integrity sha512-tRyMliepY11Ym6hB1rAFSNGwQDpmszvWYJvlK1E+md4SW8i6ylNHtpZjaYFff9Mdk3i/Pg8ItQq9P0daOJAvQw== dependencies: "@docusaurus/core" "2.4.3" @@ -1541,7 +1567,7 @@ "@docusaurus/theme-classic@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/theme-classic/-/theme-classic-2.4.3.tgz#29360f2eb03a0e1686eb19668633ef313970ee8f" + resolved "https://registry.npmmirror.com/@docusaurus/theme-classic/-/theme-classic-2.4.3.tgz" integrity sha512-QKRAJPSGPfDY2yCiPMIVyr+MqwZCIV2lxNzqbyUW0YkrlmdzzP3WuQJPMGLCjWgQp/5c9kpWMvMxjhpZx1R32Q== dependencies: "@docusaurus/core" "2.4.3" @@ -1572,7 +1598,7 @@ "@docusaurus/theme-common@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/theme-common/-/theme-common-2.4.3.tgz#bb31d70b6b67d0bdef9baa343192dcec49946a2e" + resolved "https://registry.npmmirror.com/@docusaurus/theme-common/-/theme-common-2.4.3.tgz" integrity sha512-7KaDJBXKBVGXw5WOVt84FtN8czGWhM0lbyWEZXGp8AFfL6sZQfRTluFp4QriR97qwzSyOfQb+nzcDZZU4tezUw== dependencies: "@docusaurus/mdx-loader" "2.4.3" @@ -1592,9 +1618,9 @@ use-sync-external-store "^1.2.0" utility-types "^3.10.0" -"@docusaurus/theme-search-algolia@2.4.3", "@docusaurus/theme-search-algolia@^2.4.3": +"@docusaurus/theme-search-algolia@^2.4.3", "@docusaurus/theme-search-algolia@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.3.tgz#32d4cbefc3deba4112068fbdb0bde11ac51ece53" + resolved "https://registry.npmmirror.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.3.tgz" integrity sha512-jziq4f6YVUB5hZOB85ELATwnxBz/RmSLD3ksGQOLDPKVzat4pmI8tddNWtriPpxR04BNT+ZfpPUMFkNFetSW1Q== dependencies: "@docsearch/react" "^3.1.1" @@ -1616,15 +1642,15 @@ "@docusaurus/theme-translations@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/theme-translations/-/theme-translations-2.4.3.tgz#91ac73fc49b8c652b7a54e88b679af57d6ac6102" + resolved "https://registry.npmmirror.com/@docusaurus/theme-translations/-/theme-translations-2.4.3.tgz" integrity sha512-H4D+lbZbjbKNS/Zw1Lel64PioUAIT3cLYYJLUf3KkuO/oc9e0QCVhIYVtUI2SfBCF2NNdlyhBDQEEMygsCedIg== dependencies: fs-extra "^10.1.0" tslib "^2.4.0" -"@docusaurus/types@2.4.3": +"@docusaurus/types@*", "@docusaurus/types@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/types/-/types-2.4.3.tgz#4aead281ca09f721b3c0a9b926818450cfa3db31" + resolved "https://registry.npmmirror.com/@docusaurus/types/-/types-2.4.3.tgz" integrity sha512-W6zNLGQqfrp/EoPD0bhb9n7OobP+RHpmvVzpA+Z/IuU3Q63njJM24hmT0GYboovWcDtFmnIJC9wcyx4RVPQscw== dependencies: "@types/history" "^4.7.11" @@ -1638,14 +1664,14 @@ "@docusaurus/utils-common@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/utils-common/-/utils-common-2.4.3.tgz#30656c39ef1ce7e002af7ba39ea08330f58efcfb" + resolved "https://registry.npmmirror.com/@docusaurus/utils-common/-/utils-common-2.4.3.tgz" integrity sha512-/jascp4GbLQCPVmcGkPzEQjNaAk3ADVfMtudk49Ggb+131B1WDD6HqlSmDf8MxGdy7Dja2gc+StHf01kiWoTDQ== dependencies: tslib "^2.4.0" "@docusaurus/utils-validation@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/utils-validation/-/utils-validation-2.4.3.tgz#8122c394feef3e96c73f6433987837ec206a63fb" + resolved "https://registry.npmmirror.com/@docusaurus/utils-validation/-/utils-validation-2.4.3.tgz" integrity sha512-G2+Vt3WR5E/9drAobP+hhZQMaswRwDlp6qOMi7o7ZypB+VO7N//DZWhZEwhcRGepMDJGQEwtPv7UxtYwPL9PBw== dependencies: "@docusaurus/logger" "2.4.3" @@ -1656,7 +1682,7 @@ "@docusaurus/utils@2.4.3": version "2.4.3" - resolved "https://registry.npmmirror.com/@docusaurus/utils/-/utils-2.4.3.tgz#52b000d989380a2125831b84e3a7327bef471e89" + resolved "https://registry.npmmirror.com/@docusaurus/utils/-/utils-2.4.3.tgz" integrity sha512-fKcXsjrD86Smxv8Pt0TBFqYieZZCPh4cbf9oszUq/AMhZn3ujwpKaVYZACPX8mmjtYx0JOgNx52CREBfiGQB4A== dependencies: "@docusaurus/logger" "2.4.3" @@ -1690,7 +1716,7 @@ "@headlessui/react@^1.7.17": version "1.7.17" - resolved "https://registry.npmmirror.com/@headlessui/react/-/react-1.7.17.tgz#a0ec23af21b527c030967245fd99776aa7352bc6" + resolved "https://registry.npmmirror.com/@headlessui/react/-/react-1.7.17.tgz" integrity sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow== dependencies: client-only "^0.0.1" @@ -1741,16 +1767,16 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.15" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz" @@ -1807,7 +1833,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -1915,7 +1941,7 @@ "@svgr/babel-plugin-transform-react-native-svg" "^6.5.1" "@svgr/babel-plugin-transform-svg-component" "^6.5.1" -"@svgr/core@^6.5.1": +"@svgr/core@*", "@svgr/core@^6.0.0", "@svgr/core@^6.5.1": version "6.5.1" resolved "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz" integrity sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw== @@ -2110,12 +2136,7 @@ dependencies: "@types/unist" "*" -"@types/mime@*": - version "3.0.1" - resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz" - integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== - -"@types/mime@^1": +"@types/mime@*", "@types/mime@^1": version "1.3.2" resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== @@ -2181,7 +2202,7 @@ "@types/history" "^4.7.11" "@types/react" "*" -"@types/react@*": +"@types/react@*", "@types/react@>= 16.8.0 < 19.0.0": version "18.2.7" resolved "https://registry.npmjs.org/@types/react/-/react-18.2.7.tgz" integrity sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw== @@ -2261,7 +2282,7 @@ dependencies: "@types/yargs-parser" "*" -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": +"@webassemblyjs/ast@^1.11.5", "@webassemblyjs/ast@1.11.6": version "1.11.6" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz" integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== @@ -2362,7 +2383,7 @@ "@webassemblyjs/wasm-gen" "1.11.6" "@webassemblyjs/wasm-parser" "1.11.6" -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": +"@webassemblyjs/wasm-parser@^1.11.5", "@webassemblyjs/wasm-parser@1.11.6": version "1.11.6" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz" integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== @@ -2410,7 +2431,7 @@ acorn-walk@^8.0.0: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^8.0.4, acorn@^8.5.0, acorn@^8.7.1: +acorn@^8, acorn@^8.0.4, acorn@^8.5.0, acorn@^8.7.1: version "8.8.2" resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== @@ -2447,7 +2468,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2457,7 +2478,17 @@ ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.9.0: +ajv@^8.0.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ajv@^8.8.2, ajv@^8.9.0: version "8.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -2474,7 +2505,7 @@ algoliasearch-helper@^3.10.0: dependencies: "@algolia/events" "^4.0.1" -algoliasearch@^4.0.0, algoliasearch@^4.13.1: +algoliasearch@^4.0.0, algoliasearch@^4.13.1, "algoliasearch@>= 3.1 < 6", "algoliasearch@>= 4.9.1 < 6": version "4.17.1" resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.17.1.tgz" integrity sha512-4GDQ1RhP2qUR3x8PevFRbEdqZqIARNViZYjgTJmA1T7wRNtFA3W4Aqc/RsODqa1J8IO/QDla5x4tWuUS8NV8wA== @@ -2542,7 +2573,7 @@ ansi-styles@^6.1.0: any-promise@^1.0.0: version "1.3.0" - resolved "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + resolved "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz" integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== anymatch@~3.1.2: @@ -2570,16 +2601,16 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - array-flatten@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + array-union@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" @@ -2595,21 +2626,9 @@ at-least-node@^1.0.0: resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -autoprefixer@^10.4.12, autoprefixer@^10.4.7: - version "10.4.14" - resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz" - integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ== - dependencies: - browserslist "^4.21.5" - caniuse-lite "^1.0.30001464" - fraction.js "^4.2.0" - normalize-range "^0.1.2" - picocolors "^1.0.0" - postcss-value-parser "^4.2.0" - -autoprefixer@^10.4.16: +autoprefixer@^10.4.12, autoprefixer@^10.4.16, autoprefixer@^10.4.7: version "10.4.16" - resolved "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" + resolved "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.16.tgz" integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== dependencies: browserslist "^4.21.10" @@ -2793,19 +2812,9 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: - version "4.21.7" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz" - integrity sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA== - dependencies: - caniuse-lite "^1.0.30001489" - electron-to-chromium "^1.4.411" - node-releases "^2.0.12" - update-browserslist-db "^1.0.11" - -browserslist@^4.21.10: +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5, "browserslist@>= 4.21.0": version "4.22.1" - resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" + resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.22.1.tgz" integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== dependencies: caniuse-lite "^1.0.30001541" @@ -2862,7 +2871,7 @@ camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" -camelcase-css@2.0.1, camelcase-css@^2.0.1: +camelcase-css@^2.0.1, camelcase-css@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== @@ -2882,14 +2891,9 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001489: - version "1.0.30001491" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001491.tgz" - integrity sha512-17EYIi4TLnPiTzVKMveIxU5ETlxbSO3B6iPvMbprqnKh4qJsQGk5Nh1Lp4jIMAE0XfrujsJuWZAM3oJdMHaKBA== - -caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541: version "1.0.30001558" - resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz#d2c6e21fdbfe83817f70feab902421a19b7983ee" + resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz" integrity sha512-/Et7DwLqpjS47JPEcz6VnxU9PwcIdVi0ciLXRWBQdj1XFye68pSQYpV0QtPTfUKWuOaEig+/Vez2l74eDc1tPQ== ccount@^1.0.0: @@ -2954,7 +2958,7 @@ cheerio@^1.0.0-rc.12: parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2, chokidar@^3.5.3: +chokidar@^3.4.2, chokidar@^3.5.3, "chokidar@>=3.0.0 <4.0.0": version "3.5.3" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3017,7 +3021,7 @@ cli-table3@^0.6.2: client-only@^0.0.1: version "0.0.1" - resolved "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + resolved "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== clone-deep@^4.0.1: @@ -3060,16 +3064,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + colord@^2.9.1: version "2.9.3" resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" @@ -3097,7 +3101,7 @@ commander@^2.20.0: commander@^4.0.0: version "4.1.1" - resolved "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + resolved "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== commander@^5.1.0: @@ -3435,20 +3439,27 @@ csstype@^3.0.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== -debug@2.6.9, debug@^2.6.0: +debug@^2.6.0: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1: +debug@^4.1.0, debug@^4.1.1, debug@4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" @@ -3505,16 +3516,16 @@ del@^6.1.1: rimraf "^3.0.2" slash "^3.0.0" -depd@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + destroy@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" @@ -3550,7 +3561,7 @@ detect-port@^1.3.0: didyoumean@^1.2.2: version "1.2.2" - resolved "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + resolved "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz" integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== dir-glob@^3.0.1: @@ -3562,7 +3573,7 @@ dir-glob@^3.0.1: dlv@^1.1.3: version "1.1.3" - resolved "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + resolved "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz" integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== dns-equal@^1.0.0: @@ -3663,19 +3674,19 @@ dot-prop@^5.2.0: dotenv@^16.3.1: version "16.3.1" - resolved "https://registry.npmmirror.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + resolved "https://registry.npmmirror.com/dotenv/-/dotenv-16.3.1.tgz" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== -duplexer3@^0.1.4: - version "0.1.5" - resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz" - integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== - duplexer@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +duplexer3@^0.1.4: + version "0.1.5" + resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz" + integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" @@ -3686,14 +3697,9 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.411: - version "1.4.413" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.413.tgz" - integrity sha512-Gd+/OAhRca06dkVxIQo/W7dr6Nmk9cx6lQdZ19GvFp51k5B/lUAokm6SJfNkdV8kFLsC3Z4sLTyEHWCnB1Efbw== - electron-to-chromium@^1.4.535: version "1.4.571" - resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.571.tgz#8aa71539eb82db98740c3ec861256cc34e0356fd" + resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.571.tgz" integrity sha512-Sc+VtKwKCDj3f/kLBjdyjMpNzoZsU6WuL/wFb6EH8USmHEcebxRXcRrVpOpayxd52tuey4RUDpUsw5OS5LhJqg== emoji-regex@^8.0.0: @@ -3915,20 +3921,9 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.11, fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-glob@^3.3.0: +fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.1" - resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.1.tgz" integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -3995,7 +3990,7 @@ feed@^4.2.2: dependencies: xml-js "^1.6.11" -file-loader@^6.2.0: +file-loader@*, file-loader@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz" integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== @@ -4097,14 +4092,9 @@ forwarded@0.2.0: resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fraction.js@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" - integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== - fraction.js@^4.3.6: version "4.3.7" - resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== fresh@0.5.2: @@ -4141,21 +4131,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" @@ -4207,7 +4187,14 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1, glob-parent@^6.0.2: +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -4219,27 +4206,27 @@ glob-to-regexp@^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== -glob@7.1.6: - version "7.1.6" - resolved "https://registry.npmmirror.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: - version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== +glob@7.1.6: + version "7.1.6" + resolved "https://registry.npmmirror.com/glob/-/glob-7.1.6.tgz" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.1.1" + minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" @@ -4377,13 +4364,6 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hasown@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" - integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== - dependencies: - function-bind "^1.1.2" - hast-to-hyperscript@^9.0.0: version "9.0.1" resolved "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz" @@ -4555,6 +4535,16 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-errors@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" @@ -4566,16 +4556,6 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" @@ -4676,7 +4656,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2, inherits@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4686,16 +4666,16 @@ inherits@2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -ini@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + inline-style-parser@0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz" @@ -4713,17 +4693,17 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -ipaddr.js@1.9.1: - 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== - ipaddr.js@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== -is-alphabetical@1.0.4, is-alphabetical@^1.0.0: +ipaddr.js@1.9.1: + 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== + +is-alphabetical@^1.0.0, is-alphabetical@1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz" integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== @@ -4767,13 +4747,6 @@ is-core-module@^2.11.0: dependencies: has "^1.0.3" -is-core-module@^2.13.0: - version "2.13.1" - resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== - dependencies: - hasown "^2.0.0" - is-decimal@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz" @@ -4908,16 +4881,16 @@ is-yarn-global@^0.3.0: resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== - isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" @@ -4959,14 +4932,9 @@ jest-worker@^29.1.2: merge-stream "^2.0.0" supports-color "^8.0.0" -jiti@^1.18.2: - version "1.18.2" - resolved "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz" - integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== - -jiti@^1.19.1: +jiti@^1.18.2, jiti@^1.19.1: version "1.21.0" - resolved "https://registry.npmmirror.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" + resolved "https://registry.npmmirror.com/jiti/-/jiti-1.21.0.tgz" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== joi@^17.6.0: @@ -5157,7 +5125,7 @@ lodash.memoize@^4.1.2: resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -lodash.uniq@4.5.0, lodash.uniq@^4.5.0: +lodash.uniq@^4.5.0, lodash.uniq@4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== @@ -5300,7 +5268,7 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": +"mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== @@ -5310,14 +5278,40 @@ mime-db@~1.33.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== -mime-types@2.1.18: +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@^2.1.31: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@~2.1.17, mime-types@2.1.18: version "2.1.18" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz" integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== dependencies: mime-db "~1.33.0" -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@~2.1.24: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -5351,20 +5345,13 @@ minimalistic-assert@^1.0.0: resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@3.1.2, minimatch@^3.1.1: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.4, minimatch@^3.0.5: - version "3.0.8" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" - integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== - dependencies: - brace-expansion "^1.1.7" - minimist@^1.2.0, minimist@^1.2.5: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" @@ -5400,7 +5387,7 @@ multicast-dns@^7.2.5: mz@^2.7.0: version "2.7.0" - resolved "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + resolved "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== dependencies: any-promise "^1.0.0" @@ -5449,14 +5436,9 @@ node-forge@^1: resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-releases@^2.0.12: - version "2.0.12" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz" - integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== - node-releases@^2.0.13: version "2.0.13" - resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.13.tgz" integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== normalize-path@^3.0.0, normalize-path@~3.0.0: @@ -5505,7 +5487,7 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: object-hash@^3.0.0: version "3.0.0" - resolved "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + resolved "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== object-inspect@^1.9.0: @@ -5748,6 +5730,13 @@ path-parse@^1.0.7: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" @@ -5758,13 +5747,6 @@ path-to-regexp@2.2.1: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz" integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" @@ -5782,12 +5764,12 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: pify@^2.3.0: version "2.3.0" - resolved "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + resolved "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== pirates@^4.0.1: version "4.0.6" - resolved "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + resolved "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^4.1.0: @@ -5859,7 +5841,7 @@ postcss-discard-unused@^5.1.0: postcss-import@^15.1.0: version "15.1.0" - resolved "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + resolved "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz" integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== dependencies: postcss-value-parser "^4.0.0" @@ -5868,14 +5850,14 @@ postcss-import@^15.1.0: postcss-js@^4.0.1: version "4.0.1" - resolved "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + resolved "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.0.1.tgz" integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== dependencies: camelcase-css "^2.0.1" postcss-load-config@^4.0.1: version "4.0.1" - resolved "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz#152383f481c2758274404e4962743191d73875bd" + resolved "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz" integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== dependencies: lilconfig "^2.0.5" @@ -5979,7 +5961,7 @@ postcss-modules-values@^4.0.0: postcss-nested@^6.0.1: version "6.0.1" - resolved "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" + resolved "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.0.1.tgz" integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== dependencies: postcss-selector-parser "^6.0.11" @@ -6117,18 +6099,9 @@ postcss-zindex@^5.1.0: resolved "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz" integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A== -postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.17, postcss@^8.4.21: - version "8.4.24" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz" - integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== - dependencies: - nanoid "^3.3.6" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -postcss@^8.4.23, postcss@^8.4.31: +"postcss@^7.0.0 || ^8.0.1", postcss@^8.0.0, postcss@^8.0.9, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.2.15, postcss@^8.2.2, postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.16, postcss@^8.4.17, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.31, postcss@>=8.0.9: version "8.4.31" - resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz" integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: nanoid "^3.3.6" @@ -6158,7 +6131,7 @@ prism-react-renderer@^1.3.5: resolved "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz" integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg== -prismjs@^1.28.0, prismjs@^1.29.0: +prismjs@^1.18.0, prismjs@^1.28.0, prismjs@^1.29.0: version "1.29.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz" integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== @@ -6263,16 +6236,21 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" - integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -range-parser@^1.2.1, range-parser@~1.2.1: +range-parser@~1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== + raw-body@2.5.1: version "2.5.1" resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" @@ -6283,7 +6261,7 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" -rc@1.2.8, rc@^1.2.8: +rc@^1.2.8, rc@1.2.8: version "1.2.8" resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -6333,7 +6311,7 @@ react-dev-utils@^12.0.1: strip-ansi "^6.0.1" text-table "^0.2.0" -react-dom@^17.0.2: +react-dom@*, "react-dom@^16 || ^17 || ^18", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.4 || ^17.0.0", "react-dom@^17.0.0 || ^16.3.0 || ^15.5.4", react-dom@^17.0.2, "react-dom@>= 16.8.0 < 19.0.0": version "17.0.2" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -6365,7 +6343,7 @@ react-helmet-async@*, react-helmet-async@^1.3.0: react-helmet@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + resolved "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz" integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== dependencies: object-assign "^4.1.1" @@ -6400,9 +6378,9 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1: dependencies: "@babel/runtime" "^7.10.3" -"react-loadable@npm:@docusaurus/react-loadable@5.5.2": +react-loadable@*, "react-loadable@npm:@docusaurus/react-loadable@5.5.2": version "5.5.2" - resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" + resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz" integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== dependencies: "@types/react" "*" @@ -6428,7 +6406,7 @@ react-router-dom@^5.3.3: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.3.4, react-router@^5.3.3: +react-router@^5.3.3, react-router@>=5, react-router@5.3.4: version "5.3.4" resolved "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz" integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA== @@ -6445,7 +6423,7 @@ react-router@5.3.4, react-router@^5.3.3: react-side-effect@^2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" + resolved "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz" integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== react-textarea-autosize@^8.3.2: @@ -6457,7 +6435,7 @@ react-textarea-autosize@^8.3.2: use-composed-ref "^1.3.0" use-latest "^1.2.1" -react@^17.0.2: +react@*, "react@^15.0.2 || ^16.0.0 || ^17.0.0", "react@^16 || ^17 || ^18", "react@^16.13.1 || ^17.0.0", "react@^16.3.0 || ^17.0.0 || ^18.0.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.4 || ^17.0.0", "react@^17.0.0 || ^16.3.0 || ^15.5.4", react@^17.0.2, "react@>= 16.8.0 < 19.0.0", react@>=0.14.9, react@>=15, react@>=16.3.0, react@17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -6467,7 +6445,7 @@ react@^17.0.2: read-cache@^1.0.0: version "1.0.0" - resolved "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + resolved "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz" integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== dependencies: pify "^2.3.0" @@ -6680,7 +6658,7 @@ resolve-pathname@^3.0.0: resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz" integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== -resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.22.2, resolve@^1.3.2: version "1.22.2" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz" integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== @@ -6689,15 +6667,6 @@ resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.1.7, resolve@^1.22.2: - version "1.22.8" - resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - responselike@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" @@ -6751,15 +6720,20 @@ rxjs@^7.5.4: dependencies: tslib "^2.1.0" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" @@ -6777,7 +6751,7 @@ sass-loader@^10.1.1: schema-utils "^3.0.0" semver "^7.3.2" -sass@^1.62.1: +sass@^1.3.0, sass@^1.30.0, sass@^1.62.1: version "1.62.1" resolved "https://registry.npmjs.org/sass/-/sass-1.62.1.tgz" integrity sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A== @@ -6799,15 +6773,6 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== - dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" - schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" @@ -6817,7 +6782,25 @@ schema-utils@^2.6.5: ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.1.2: +schema-utils@^3.0.0: + version "3.1.2" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz" + integrity sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz" + integrity sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz" integrity sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg== @@ -6836,6 +6819,15 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + section-matter@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz" @@ -6868,7 +6860,27 @@ semver@^5.4.1: resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^6.1.1: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^6.1.2: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^6.3.0: version "6.3.0" resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -7058,7 +7070,7 @@ sort-css-media-queries@2.1.0: resolved "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz" integrity sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA== -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: +source-map-js@^1.0.2, "source-map-js@>=0.6.2 <2.0.0": version "1.0.2" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== @@ -7124,22 +7136,45 @@ state-toggle@^1.0.0: resolved "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz" integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - "statuses@>= 1.4.0 < 2": version "1.5.0" resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + std-env@^3.0.1: version "3.3.3" resolved "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz" integrity sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg== -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: +string_decoder@^1.1.1: + 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== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.2: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.2.0: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7157,20 +7192,6 @@ string-width@^5.0.1: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string_decoder@^1.1.1: - 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== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" @@ -7214,7 +7235,7 @@ strip-json-comments@~2.0.1: resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -style-to-object@0.3.0, style-to-object@^0.3.0: +style-to-object@^0.3.0, style-to-object@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz" integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== @@ -7231,7 +7252,7 @@ stylehacks@^5.1.1: sucrase@^3.32.0: version "3.34.0" - resolved "https://registry.npmmirror.com/sucrase/-/sucrase-3.34.0.tgz#1e0e2d8fcf07f8b9c3569067d92fbd8690fb576f" + resolved "https://registry.npmmirror.com/sucrase/-/sucrase-3.34.0.tgz" integrity sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== dependencies: "@jridgewell/gen-mapping" "^0.3.2" @@ -7288,7 +7309,7 @@ svgo@^2.7.0, svgo@^2.8.0: tailwindcss@^3.3.3: version "3.3.5" - resolved "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.3.5.tgz#22a59e2fbe0ecb6660809d9cc5f3976b077be3b8" + resolved "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.3.5.tgz" integrity sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA== dependencies: "@alloc/quick-lru" "^5.2.0" @@ -7352,14 +7373,14 @@ text-table@^0.2.0: thenify-all@^1.0.0: version "1.6.0" - resolved "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + resolved "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz" integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== dependencies: thenify ">= 3.1.0 < 4" "thenify@>= 3.1.0 < 4": version "3.3.1" - resolved "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + resolved "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz" integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== dependencies: any-promise "^1.0.0" @@ -7428,7 +7449,7 @@ trough@^1.0.0: ts-interface-checker@^0.1.9: version "0.1.13" - resolved "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + resolved "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: @@ -7461,7 +7482,7 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^5.0.4: +typescript@^5.0.4, "typescript@>= 2.7": version "5.0.4" resolved "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz" integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== @@ -7502,10 +7523,10 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -unified@9.2.0: - version "9.2.0" - resolved "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz" - integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== +unified@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz" + integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== dependencies: bail "^1.0.0" extend "^3.0.0" @@ -7514,10 +7535,10 @@ unified@9.2.0: trough "^1.0.0" vfile "^4.0.0" -unified@^9.2.2: - version "9.2.2" - resolved "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz" - integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== +unified@9.2.0: + version "9.2.0" + resolved "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz" + integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== dependencies: bail "^1.0.0" extend "^3.0.0" @@ -7533,7 +7554,7 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -unist-builder@2.0.3, unist-builder@^2.0.0: +unist-builder@^2.0.0, unist-builder@2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz" integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== @@ -7582,7 +7603,7 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" -unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.3: +unist-util-visit@^2.0.0, unist-util-visit@^2.0.3, unist-util-visit@2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz" integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== @@ -7596,22 +7617,14 @@ universalify@^2.0.0: resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0, unpipe@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - update-browserslist-db@^1.0.13: version "1.0.13" - resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz" integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== dependencies: escalade "^3.1.1" @@ -7689,7 +7702,7 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: utila@~0.4: version "0.4.0" - resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz" integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== utility-types@^3.10.0: @@ -7852,7 +7865,7 @@ webpack-sources@^3.2.2, webpack-sources@^3.2.3: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.73.0: +"webpack@^4.0.0 || ^5.0.0", "webpack@^4.36.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", webpack@^5.0.0, webpack@^5.1.0, webpack@^5.20.0, webpack@^5.73.0, "webpack@>= 4", webpack@>=2, "webpack@>=4.41.1 || 5.x", "webpack@3 || 4 || 5": version "5.84.1" resolved "https://registry.npmjs.org/webpack/-/webpack-5.84.1.tgz" integrity sha512-ZP4qaZ7vVn/K8WN/p990SGATmrL1qg4heP/MrVneczYtpDGJWlrgZv55vxaV2ul885Kz+25MP2kSXkPe3LZfmg== @@ -7892,7 +7905,7 @@ webpackbar@^5.0.2: pretty-time "^1.1.0" std-env "^3.0.1" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: +websocket-driver@^0.7.4, websocket-driver@>=0.5.1: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== @@ -8031,7 +8044,7 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: yaml@^2.1.1: version "2.3.3" - resolved "https://registry.npmmirror.com/yaml/-/yaml-2.3.3.tgz#01f6d18ef036446340007db8e016810e5d64aad9" + resolved "https://registry.npmmirror.com/yaml/-/yaml-2.3.3.tgz" integrity sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ== yocto-queue@^0.1.0: diff --git a/extensions/ide/vscode/devbox/CHANGELOG.md b/extensions/ide/vscode/devbox/CHANGELOG.md index 4e427795af5..bec8e4d2259 100644 --- a/extensions/ide/vscode/devbox/CHANGELOG.md +++ b/extensions/ide/vscode/devbox/CHANGELOG.md @@ -6,10 +6,28 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## [Unreleased] +## [1.3.0] - 2024-12-24 + +### Fixed + +- Fix http url -> https url. + +## [1.2.3] - 2024-12-24 + +### Fixed + +- Fix Windows file authority issue caused by `Everyone` group. + +## [1.2.2] - 2024-12-09 + ### Changed - Adjust `Remote-SSH` to install by code. +### Fixed + +- Fix Windows file authority issue caused by `Everyone` group. + ## [1.2.1] - 2024-12-4 ### Fixed diff --git a/extensions/ide/vscode/devbox/package.json b/extensions/ide/vscode/devbox/package.json index ecc9d429b93..5329d79ee14 100644 --- a/extensions/ide/vscode/devbox/package.json +++ b/extensions/ide/vscode/devbox/package.json @@ -2,7 +2,7 @@ "name": "devbox-aio", "displayName": "%displayName%", "description": "%description%", - "version": "1.3.2024120601", + "version": "1.3.0", "keywords": [ "devbox", "remote development", diff --git a/extensions/ide/vscode/devbox/src/extension.ts b/extensions/ide/vscode/devbox/src/extension.ts index d512c644974..6aba1304866 100644 --- a/extensions/ide/vscode/devbox/src/extension.ts +++ b/extensions/ide/vscode/devbox/src/extension.ts @@ -37,7 +37,7 @@ export async function activate(context: vscode.ExtensionContext) { const remoteUri = workspaceFolder.uri.authority const devboxId = remoteUri.replace(/^ssh-remote\+/, '') // devbox = sshHostLabel const region = GlobalStateManager.getRegion(devboxId) - updateBaseUrl(`http://devbox.${region}`) + updateBaseUrl(`https://devbox.${region}`) } // network view diff --git a/extensions/ide/vscode/devbox/src/providers/DBViewProvider.ts b/extensions/ide/vscode/devbox/src/providers/DBViewProvider.ts index b1c99f5a7ca..d97f38fa830 100644 --- a/extensions/ide/vscode/devbox/src/providers/DBViewProvider.ts +++ b/extensions/ide/vscode/devbox/src/providers/DBViewProvider.ts @@ -83,7 +83,7 @@ export class DBViewProvider const remoteUri = workspaceFolder.uri.authority const devboxId = remoteUri.replace(/^ssh-remote\+/, '') // devbox = sshHostLabel const region = GlobalStateManager.getRegion(devboxId) - targetUrl = `http://${region}?openapp=system-dbprovider` + targetUrl = `https://${region}?openapp=system-dbprovider` this._register( vscode.commands.registerCommand('devbox.gotoDatabaseWebPage', () => { vscode.commands.executeCommand('devbox.openExternalLink', [ @@ -143,7 +143,7 @@ export class DBViewProvider const remoteUri = workspaceFolder.uri.authority const devboxId = remoteUri.replace(/^ssh-remote\+/, '') // devbox = sshHostLabel const region = GlobalStateManager.getRegion(devboxId) - targetUrl = `http://${region}?openapp=system-terminal?defaultCommand=${targetCommand}` + targetUrl = `https://${region}?openapp=system-terminal?defaultCommand=${targetCommand}` vscode.commands.executeCommand('devbox.openExternalLink', [targetUrl]) } } diff --git a/extensions/ide/vscode/devbox/src/utils/file.ts b/extensions/ide/vscode/devbox/src/utils/file.ts index b6b888b19c5..f1daf601a80 100644 --- a/extensions/ide/vscode/devbox/src/utils/file.ts +++ b/extensions/ide/vscode/devbox/src/utils/file.ts @@ -13,9 +13,9 @@ export const ensureFileAccessPermission = async (path: string) => { if (!username) { throw new Error('can not get username') } - // await execa('icacls', [path, '/inheritance:r']) // await execa('icacls', [path, '/grant:r', `${username}:F`]) - // await execa('icacls', [path, '/remove:g', 'everyone']) + await execa('icacls', [path, '/inheritance:d']) + await execa('icacls', [path, '/remove:g', 'everyone']) } catch (error) { Logger.error(`Failed to set file access permission: ${error}`) } @@ -40,7 +40,7 @@ export function ensureFileExists(filePath: string, parentDir: string) { recursive: true, }) fs.writeFileSync(filePath, '', 'utf8') - // .ssh/config authority - ensureFileAccessPermission(filePath) } + // .ssh/config authority + ensureFileAccessPermission(filePath) } diff --git a/frontend/desktop/src/api/platform.ts b/frontend/desktop/src/api/platform.ts index b37d0347a52..313000b532f 100644 --- a/frontend/desktop/src/api/platform.ts +++ b/frontend/desktop/src/api/platform.ts @@ -92,6 +92,7 @@ export const getResource = () => { totalMemory: string; totalStorage: string; runningPodCount: string; + totalGpuCount: string; totalPodCount: string; }> >('/api/desktop/getResource'); diff --git a/frontend/desktop/src/components/account/AccountCenter/index.tsx b/frontend/desktop/src/components/account/AccountCenter/index.tsx index 43a29c05a5e..8d70dd22dda 100644 --- a/frontend/desktop/src/components/account/AccountCenter/index.tsx +++ b/frontend/desktop/src/components/account/AccountCenter/index.tsx @@ -263,7 +263,7 @@ export default function Index(props: Omit) { } /> )} - {conf.authConfig?.idp.sms.enabled && ( + {conf.authConfig?.idp.sms.enabled && conf.authConfig.idp.sms.ali.enabled && ( {t('common:phone')}} RightElement={ @@ -299,40 +299,42 @@ export default function Index(props: Omit) { } /> )} - {t('common:email')}} - RightElement={ - <> - - {providerState.EMAIL.isBinding - ? providerState.EMAIL.id.replace(/(\d{3})\d+(\d{4})/, '$1****$2') - : t('common:unbound')} - - - { - providerState.EMAIL.isBinding - ? setPageState(PageState.EMAIL_CHANGE_BIND) - : setPageState(PageState.EMAIL_BIND); - }} - /> - {providerState.EMAIL.isBinding && providerState.total > 1 && ( + {conf.authConfig?.idp.sms.enabled && conf.authConfig.idp.sms.email.enabled && ( + {t('common:email')}} + RightElement={ + <> + + {providerState.EMAIL.isBinding + ? providerState.EMAIL.id.replace(/(\d{3})\d+(\d{4})/, '$1****$2') + : t('common:unbound')} + + { - setPageState(PageState.EMAIL_UNBIND); + providerState.EMAIL.isBinding + ? setPageState(PageState.EMAIL_CHANGE_BIND) + : setPageState(PageState.EMAIL_BIND); }} /> - )} - - - } - /> + {providerState.EMAIL.isBinding && providerState.total > 1 && ( + { + setPageState(PageState.EMAIL_UNBIND); + }} + /> + )} + + + } + /> + )} 1.1 * appHeaderWidth + : x > 0.9 * appHeaderWidth ? 0 : x, y: y < upperBoundary ? upperBoundary : y > lowerBoundary ? 0 : y @@ -92,7 +92,7 @@ export default function AppWindow(props: { { diff --git a/frontend/desktop/src/components/desktop_content/monitor.tsx b/frontend/desktop/src/components/desktop_content/monitor.tsx index 53c7d0b791a..5cbeaf58d5a 100644 --- a/frontend/desktop/src/components/desktop_content/monitor.tsx +++ b/frontend/desktop/src/components/desktop_content/monitor.tsx @@ -3,8 +3,9 @@ import { Box, CircularProgress, CircularProgressLabel, Flex, Text } from '@chakr import { MonitorIcon } from '@sealos/ui'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; -import { CpuIcon, FlowIcon, MemoryIcon, StorageIcon } from '../icons'; +import { CpuIcon, FlowIcon, GpuIcon, MemoryIcon, StorageIcon } from '../icons'; import { blurBackgroundStyles } from './index'; +import { useMemo } from 'react'; export default function Monitor({ needStyles = true }: { needStyles?: boolean }) { const { t } = useTranslation(); @@ -12,32 +13,45 @@ export default function Monitor({ needStyles = true }: { needStyles?: boolean }) staleTime: 60 * 1000 }); - const info = [ - { - label: 'CPU', - value: data?.data?.totalCpu, - icon: , - unit: 'C' - }, - { - label: t('common:memory'), - value: data?.data?.totalMemory, - icon: , - unit: 'GB' - }, - { - label: t('common:storage'), - value: data?.data?.totalStorage, - icon: , - unit: 'GB' - }, - { - label: t('common:flow'), - value: `~`, - icon: , - unit: 'GB' - } - ]; + const info = useMemo( + () => [ + { + label: 'CPU', + value: data?.data?.totalCpu, + icon: , + unit: 'C' + }, + { + label: t('common:memory'), + value: data?.data?.totalMemory, + icon: , + unit: 'GB' + }, + { + label: t('common:storage'), + value: data?.data?.totalStorage, + icon: , + unit: 'GB' + }, + { + label: t('common:flow'), + value: `~`, + icon: , + unit: 'GB' + }, + ...(Number(data?.data?.totalGpuCount) > 0 + ? [ + { + label: 'GPU', + value: data?.data?.totalGpuCount, + icon: , + unit: 'Card' + } + ] + : []) + ], + [data?.data, t] + ); const totalPodCount = Number(data?.data?.totalPodCount) || 0; const runningPodCount = Number(data?.data?.runningPodCount) || 0; diff --git a/frontend/desktop/src/components/icons/index.tsx b/frontend/desktop/src/components/icons/index.tsx index 5e42b63ca27..54aa7322e44 100644 --- a/frontend/desktop/src/components/icons/index.tsx +++ b/frontend/desktop/src/components/icons/index.tsx @@ -605,3 +605,24 @@ export function AttachmentIcon(props: IconProps) { ); } + +export function GpuIcon(props: IconProps) { + return ( + + + + + + ); +} diff --git a/frontend/desktop/src/components/signin/index.tsx b/frontend/desktop/src/components/signin/index.tsx index 0b20f613347..1c4ed0e5a57 100644 --- a/frontend/desktop/src/components/signin/index.tsx +++ b/frontend/desktop/src/components/signin/index.tsx @@ -31,11 +31,8 @@ export default function SigninComponent() { const conf = useConfigStore(); const hasBaiduToken = conf.authConfig?.hasBaiduToken; const needPassword = conf.authConfig?.idp.password?.enabled; - const needSms = conf.authConfig?.idp.sms?.enabled; - const needTabsCount = - 0 + - (conf.authConfig?.idp.password?.enabled ? 1 : 0) + - (conf.authConfig?.idp.sms?.enabled ? 1 : 0); + const needPhone = conf.authConfig?.idp.sms?.enabled && conf.authConfig.idp.sms.ali.enabled; + const needTabsCount = 0 + (conf.authConfig?.idp.password?.enabled ? 1 : 0) + (needPhone ? 1 : 0); const disclosure = useDisclosure(); const { t, i18n } = useTranslation(); const [tabIndex, setTabIndex] = useState(LoginType.NONE); @@ -113,8 +110,8 @@ export default function SigninComponent() { ]); useEffect(() => { - setTabIndex(needSms ? LoginType.SMS : needPassword ? LoginType.PASSWORD : LoginType.NONE); - }, [needPassword, needSms]); + setTabIndex(needPhone ? LoginType.SMS : needPassword ? LoginType.PASSWORD : LoginType.NONE); + }, [needPassword, needPhone]); const LoginComponent = useMemo( () => (tabIndex !== LoginType.NONE ? loginConfig[tabIndex].component : null), diff --git a/frontend/desktop/src/pages/api/desktop/getResource.ts b/frontend/desktop/src/pages/api/desktop/getResource.ts index 8e569580b53..784281b5975 100644 --- a/frontend/desktop/src/pages/api/desktop/getResource.ts +++ b/frontend/desktop/src/pages/api/desktop/getResource.ts @@ -23,6 +23,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) let totalMemoryLimits = 0; let totalStorageRequests = 0; let runningPodCount = 0; + let totalGpuCount = 0; let totalPodCount = 0; for (const pod of result.body.items) { @@ -40,9 +41,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const limits = container?.resources.limits as { cpu: string; memory: string; + ['nvidia.com/gpu']?: string; }; totalCpuLimits += parseResourceValue(limits.cpu); totalMemoryLimits += parseResourceValue(limits.memory); + + totalGpuCount += Number(limits['nvidia.com/gpu'] || 0); } if (!pod?.spec?.volumes) continue; @@ -65,7 +69,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) totalMemory: totalMemoryLimits.toFixed(2), totalStorage: totalStorageRequests.toFixed(2), runningPodCount, - totalPodCount + totalPodCount, + totalGpuCount // result: result.body.items } }); diff --git a/frontend/desktop/src/pages/api/platform/getAuthConfig.ts b/frontend/desktop/src/pages/api/platform/getAuthConfig.ts index fea71d05a62..8aeebb90fe3 100644 --- a/frontend/desktop/src/pages/api/platform/getAuthConfig.ts +++ b/frontend/desktop/src/pages/api/platform/getAuthConfig.ts @@ -29,7 +29,13 @@ function genResAuthClientConfig(conf: AuthConfigType) { enabled: !!conf.idp.password?.enabled }, sms: { - enabled: !!conf.idp.sms?.ali?.enabled + enabled: !!conf.idp.sms?.enabled, + ali: { + enabled: !!conf.idp.sms?.ali?.enabled + }, + email: { + enabled: !!conf.idp.sms?.email?.enabled + } }, github: { enabled: !!conf.idp.github?.enabled, diff --git a/frontend/desktop/src/types/system.ts b/frontend/desktop/src/types/system.ts index 2f25e2f4f7d..dac507ced71 100644 --- a/frontend/desktop/src/types/system.ts +++ b/frontend/desktop/src/types/system.ts @@ -165,7 +165,19 @@ export type AuthClientConfigType = DeepRequired< 'cloudVitrualMachineUrl' ] > ->; +> & { + idp: { + sms: { + enabled: boolean; + ali: { + enabled: boolean; + }; + email: { + enabled: boolean; + }; + }; + }; +}; export type JwtConfigType = { internal?: string; @@ -277,7 +289,13 @@ export const DefaultAuthClientConfig: AuthClientConfigType = { proxyAddress: '' }, sms: { - enabled: false + enabled: false, + ali: { + enabled: false + }, + email: { + enabled: false + } }, oauth2: { enabled: false, diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 20eca39a951..3860e245cf7 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -1487,6 +1487,12 @@ importers: '@kubernetes/client-node': specifier: ^0.21.0 version: 0.21.0 + '@prisma/client': + specifier: ^5.10.2 + version: 5.10.2(prisma@5.10.2) + '@sealos/driver': + specifier: workspace:^ + version: link:../../packages/driver '@sealos/ui': specifier: workspace:^ version: link:../../packages/ui @@ -1547,6 +1553,9 @@ importers: nprogress: specifier: ^0.2.0 version: 0.2.0 + prisma: + specifier: ^5.10.2 + version: 5.10.2 react: specifier: ^18 version: 18.2.0 @@ -1571,6 +1580,9 @@ importers: sealos-desktop-sdk: specifier: workspace:* version: link:../../packages/client-sdk + zod: + specifier: ^3.23.8 + version: 3.23.8 zustand: specifier: ^4.5.4 version: 4.5.4(@types/react@18.2.37)(immer@10.1.1)(react@18.2.0) diff --git a/frontend/providers/applaunchpad/src/pages/api/pauseApp.ts b/frontend/providers/applaunchpad/src/pages/api/pauseApp.ts index dc90275d7cf..b7fc2aeafb2 100644 --- a/frontend/providers/applaunchpad/src/pages/api/pauseApp.ts +++ b/frontend/providers/applaunchpad/src/pages/api/pauseApp.ts @@ -3,7 +3,8 @@ import { ApiResp } from '@/services/kubernet'; import { authSession } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; -import { pauseKey } from '@/constants/app'; +import { appDeployKey, pauseKey } from '@/constants/app'; +import { PatchUtils } from '@kubernetes/client-node'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -11,7 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< if (!appName) { throw new Error('appName is empty'); } - const { apiClient, k8sAutoscaling, getDeployApp, namespace } = await getK8s({ + const { apiClient, k8sAutoscaling, getDeployApp, namespace, k8sNetworkingApp } = await getK8s({ kubeconfig: await authSession(req.headers) }); @@ -47,6 +48,57 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< } } + // handle ingress + try { + const { body: ingress } = await k8sNetworkingApp.listNamespacedIngress( + namespace, + undefined, + undefined, + undefined, + undefined, + `${appDeployKey}=${appName}` + ); + if (ingress?.items?.length > 0) { + for (const ingressItem of ingress.items) { + if (ingressItem?.metadata?.name) { + const patchData: Record = {}; + if (ingressItem.metadata?.annotations?.['kubernetes.io/ingress.class'] === 'nginx') { + patchData.metadata = { + annotations: { + 'kubernetes.io/ingress.class': 'pause' + } + }; + } + if (ingressItem.spec?.ingressClassName === 'nginx') { + patchData.spec = { + ingressClassName: 'pause' + }; + } + + if (Object.keys(patchData).length > 0) { + requestQueue.push( + k8sNetworkingApp.patchNamespacedIngress( + ingressItem.metadata.name, + namespace, + patchData, + undefined, + undefined, + undefined, + undefined, + undefined, + { headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_MERGE_PATCH } } + ) + ); + } + } + } + } + } catch (error: any) { + if (error?.statusCode !== 404) { + return Promise.reject('无法读取到ingress'); + } + } + // replace source file app.metadata.annotations[pauseKey] = JSON.stringify(restartAnnotations); app.spec.replicas = 0; diff --git a/frontend/providers/applaunchpad/src/pages/api/startApp.ts b/frontend/providers/applaunchpad/src/pages/api/startApp.ts index 6784371ee8e..9c1c18bcd01 100644 --- a/frontend/providers/applaunchpad/src/pages/api/startApp.ts +++ b/frontend/providers/applaunchpad/src/pages/api/startApp.ts @@ -3,9 +3,10 @@ import { ApiResp } from '@/services/kubernet'; import { authSession } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; -import { pauseKey, minReplicasKey, maxReplicasKey } from '@/constants/app'; +import { pauseKey, minReplicasKey, maxReplicasKey, appDeployKey } from '@/constants/app'; import { json2HPA } from '@/utils/deployYaml2Json'; import { AppEditType } from '@/types/app'; +import { PatchUtils } from '@kubernetes/client-node'; /* start app. */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -14,7 +15,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< if (!appName) { throw new Error('appName is empty'); } - const { apiClient, getDeployApp, applyYamlList } = await getK8s({ + const { apiClient, getDeployApp, applyYamlList, namespace, k8sNetworkingApp } = await getK8s({ kubeconfig: await authSession(req.headers) }); @@ -60,6 +61,57 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< requestQueue.push(applyYamlList([hpaYaml], 'create')); } + // handle ingress + try { + const { body: ingress } = await k8sNetworkingApp.listNamespacedIngress( + namespace, + undefined, + undefined, + undefined, + undefined, + `${appDeployKey}=${appName}` + ); + if (ingress?.items?.length > 0) { + for (const ingressItem of ingress.items) { + if (ingressItem?.metadata?.name) { + const patchData: Record = {}; + if (ingressItem.metadata?.annotations?.['kubernetes.io/ingress.class'] === 'pause') { + patchData.metadata = { + annotations: { + 'kubernetes.io/ingress.class': 'nginx' + } + }; + } + if (ingressItem.spec?.ingressClassName === 'pause') { + patchData.spec = { + ingressClassName: 'nginx' + }; + } + + if (Object.keys(patchData).length > 0) { + requestQueue.push( + k8sNetworkingApp.patchNamespacedIngress( + ingressItem.metadata.name, + namespace, + patchData, + undefined, + undefined, + undefined, + undefined, + undefined, + { headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_MERGE_PATCH } } + ) + ); + } + } + } + } + } catch (error: any) { + if (error?.statusCode !== 404) { + return Promise.reject('无法读取到ingress'); + } + } + await Promise.all(requestQueue); jsonRes(res); diff --git a/frontend/providers/applaunchpad/src/pages/app/detail/components/AppBaseInfo.tsx b/frontend/providers/applaunchpad/src/pages/app/detail/components/AppBaseInfo.tsx index d5ad98246bb..5c6ec64d031 100644 --- a/frontend/providers/applaunchpad/src/pages/app/detail/components/AppBaseInfo.tsx +++ b/frontend/providers/applaunchpad/src/pages/app/detail/components/AppBaseInfo.tsx @@ -76,7 +76,10 @@ const AppBaseInfo = ({ app = MOCK_APP_DETAIL }: { app: AppDetailType }) => { iconName: 'deployMode', items: app.hpa.use ? [ - { label: `${app.hpa.target} ${t('target_value')}`, value: `${app.hpa.value}%` }, + { + label: `${app.hpa.target} ${t('target_value')}`, + value: `${app.hpa.value}${app.hpa.target === 'gpu' ? '' : '%'}` + }, { label: 'Number of Instances', value: `${app.hpa.minReplicas} ~ ${app.hpa.maxReplicas}` diff --git a/frontend/providers/applaunchpad/src/pages/app/edit/components/Form.tsx b/frontend/providers/applaunchpad/src/pages/app/edit/components/Form.tsx index 2a414493d23..4a73a901a89 100644 --- a/frontend/providers/applaunchpad/src/pages/app/edit/components/Form.tsx +++ b/frontend/providers/applaunchpad/src/pages/app/edit/components/Form.tsx @@ -281,13 +281,17 @@ const Form = ({ const cpuList = formSliderListConfig[key].cpu; const memoryList = formSliderListConfig[key].memory; - const sortedCpuList = - cpu !== undefined ? [...new Set([...cpuList, cpu])].sort((a, b) => a - b) : cpuList; + const sortedCpuList = !!gpuType + ? cpuList + : cpu !== undefined + ? [...new Set([...cpuList, cpu])].sort((a, b) => a - b) + : cpuList; - const sortedMemoryList = - memory !== undefined - ? [...new Set([...memoryList, memory])].sort((a, b) => a - b) - : memoryList; + const sortedMemoryList = !!gpuType + ? memoryList + : memory !== undefined + ? [...new Set([...memoryList, memory])].sort((a, b) => a - b) + : memoryList; return { cpu: sliderNumber2MarkList({ @@ -304,7 +308,7 @@ const Form = ({ }, [formSliderListConfig, getValues]); // eslint-disable-next-line react-hooks/exhaustive-deps - const SliderList = useMemo(() => countSliderList(), [already]); + const SliderList = useMemo(() => countSliderList(), [already, refresh]); return ( <> @@ -600,7 +604,7 @@ const Form = ({ } })} /> - % + {getValues('hpa.target') === 'gpu' ? '' : '%'} } @@ -675,6 +679,9 @@ const Form = ({ const inventory = countGpuInventory(type); if (type === '' || (selected && inventory > 0)) { setValue('gpu.type', type); + const sliderList = countSliderList(); + setValue('cpu', sliderList.cpu[1].value); + setValue('memory', sliderList.memory[1].value); } }} /> diff --git a/frontend/providers/applaunchpad/src/utils/adapt.ts b/frontend/providers/applaunchpad/src/utils/adapt.ts index fe7692462c9..89a60c779e8 100644 --- a/frontend/providers/applaunchpad/src/utils/adapt.ts +++ b/frontend/providers/applaunchpad/src/utils/adapt.ts @@ -126,16 +126,16 @@ export const adaptPod = (pod: V1Pod): PodDetailType => { if (container.length > 0) { const stateObj = container[0].state; if (stateObj) { - const stateKeys = Object.keys(stateObj); - const key = stateKeys[0] as `${PodStatusEnum}`; - if (key === PodStatusEnum.running) { - return podStatusMap[PodStatusEnum.running]; - } - if (key && podStatusMap[key]) { - return { - ...podStatusMap[key], - ...stateObj[key] - }; + const status = [ + PodStatusEnum.running, + PodStatusEnum.terminated, + PodStatusEnum.waiting + ].find((s) => stateObj[s]); + + if (status) { + return status === PodStatusEnum.running + ? podStatusMap[PodStatusEnum.running] + : { ...podStatusMap[status], ...stateObj[status] }; } } } @@ -146,13 +146,16 @@ export const adaptPod = (pod: V1Pod): PodDetailType => { if (container.length > 0) { const lastStateObj = container[0].lastState; if (lastStateObj) { - const lastStateKeys = Object.keys(lastStateObj); - const key = lastStateKeys[0] as `${PodStatusEnum}`; - if (key && podStatusMap[key]) { - return { - ...podStatusMap[key], - ...lastStateObj[key] - }; + const status = [ + PodStatusEnum.running, + PodStatusEnum.terminated, + PodStatusEnum.waiting + ].find((s) => lastStateObj[s]); + + if (status) { + return status === PodStatusEnum.running + ? podStatusMap[PodStatusEnum.running] + : { ...podStatusMap[status], ...lastStateObj[status] }; } } } @@ -410,7 +413,7 @@ export const sliderNumber2MarkList = ({ type: 'cpu' | 'memory'; gpuAmount?: number; }) => { - const newVal = val.map((item) => item * gpuAmount); + const newVal = val.map((item) => item); return newVal.map((item) => ({ label: type === 'memory' ? (item >= 1024 ? `${item / 1024} G` : `${item} M`) : `${item / 1000}`, diff --git a/frontend/providers/costcenter/public/locales/en/common.json b/frontend/providers/costcenter/public/locales/en/common.json index ff01868314a..d7d9220483d 100644 --- a/frontend/providers/costcenter/public/locales/en/common.json +++ b/frontend/providers/costcenter/public/locales/en/common.json @@ -1,6 +1,6 @@ { "SwitchLanguage": "Switch Language", - "Charge": "Top-ups", + "Charge": "Top Up", "Balance is insufficient": "Balance is insufficient", "Payment Status": "Payment Status", "Scan with WeChat": "Scan with WeChat", @@ -24,6 +24,7 @@ "Select Amount": "Select Amount", "Port Amount": "Port Amount", "CPU Amount": "CPU Amount", + "GPU Amount": "GPU Amount", "Storage Amount": "Storage Amount", "Memory Amount": "Memory Amount", "Network Amount": "Network Amount", @@ -49,6 +50,7 @@ "Network": "Network", "Storage": "Storage", "cpu": "CPU", + "gpu": "GPU", "memory": "Memory", "network": "Network", "storage": "Storage", @@ -67,7 +69,7 @@ "All": "All", "Total": "Total", "Page": "Page", - "View Discount Rules": "View recharge discount rules.", + "View Discount Rules": "Review Bonus Policy", "Cancel": "Cancel", "Not Enough Balance": "Insufficient balance, please recharge immediately.", "Insufficient Balance": "Insufficient balance", @@ -235,6 +237,9 @@ "usage": "usage", "resource": "resource", "Double": "Double", - "first_recharge_tips": "Partial specifications can enjoy double the amount of the initial recharge.", - "first_recharge_title": "Double on First Recharge" -} \ No newline at end of file + "first_recharge_tips": "Get double credits when making a first-time purchase for some tiers.", + "first_recharge_title": "Get Double with Initial Purchase!", + "credit_purchase": "Credit Purchase", + "remaining_balance": "Remaining Balance", + "custom_amount": "Custom Amount" +} diff --git a/frontend/providers/costcenter/public/locales/zh/common.json b/frontend/providers/costcenter/public/locales/zh/common.json index 4fbce721f53..0bca7f2394f 100644 --- a/frontend/providers/costcenter/public/locales/zh/common.json +++ b/frontend/providers/costcenter/public/locales/zh/common.json @@ -43,11 +43,13 @@ "Storage": "存储卷", "Network": "网络", "CPU Amount": "CPU金额", + "GPU Amount": "GPU金额", "Port Amount": "端口金额", "Storage Amount": "存储金额", "Memory Amount": "内存金额", "Network Amount": "网络金额", "cpu": "CPU", + "gpu": "GPU", "memory": "内存", "storage": "存储卷", "network": "网络", @@ -236,5 +238,8 @@ "resource": "资源", "Double": "双倍", "first_recharge_tips": "部分规格首次充值可享双倍赠送金额", - "first_recharge_title": "首充双倍" + "first_recharge_title": "首充双倍!", + "credit_purchase": "余额充值", + "remaining_balance": "当前余额", + "custom_amount": "输入金额" } diff --git a/frontend/providers/costcenter/src/components/CurrencySymbol.tsx b/frontend/providers/costcenter/src/components/CurrencySymbol.tsx index bb50cba8657..12095b37c78 100644 --- a/frontend/providers/costcenter/src/components/CurrencySymbol.tsx +++ b/frontend/providers/costcenter/src/components/CurrencySymbol.tsx @@ -8,10 +8,14 @@ export default function CurrencySymbol({ } & IconProps & TextProps) { return type === 'shellCoin' ? ( - + ) : type === 'cny' ? ( - + + ¥ + ) : ( - $ + + $ + ); } diff --git a/frontend/providers/costcenter/src/components/RechargeModal.tsx b/frontend/providers/costcenter/src/components/RechargeModal.tsx index 499715d0789..003a048b30b 100644 --- a/frontend/providers/costcenter/src/components/RechargeModal.tsx +++ b/frontend/providers/costcenter/src/components/RechargeModal.tsx @@ -1,7 +1,7 @@ import vector from '@/assert/Vector.svg'; import stripe_icon from '@/assert/bi_stripe.svg'; import wechat_icon from '@/assert/ic_baseline-wechat.svg'; -import { default as CurrencySymbol, default as Currencysymbol } from '@/components/CurrencySymbol'; +import CurrencySymbol from '@/components/CurrencySymbol'; import OuterLink from '@/components/outerLink'; import { useCustomToast } from '@/hooks/useCustomToast'; import useEnvStore from '@/stores/env'; @@ -77,7 +77,7 @@ function WechatPayment(props: { complete: number; codeURL?: string; tradeNO?: st px="37px" justify={'center'} align={'center'} - mt={'135px'} + m={'auto'} display={'flex'} justifyContent={'center'} alignItems={'center'} @@ -190,7 +190,7 @@ const BonusBox = (props: { {t('Double')}! + - + {props.bouns} @@ -201,7 +201,7 @@ const BonusBox = (props: { minW={'max-content'} left="78px" top="4px" - px={'13.5px'} + px={'9.5px'} py={'2.5px'} color={'purple.600'} background="purple.100" @@ -219,9 +219,9 @@ const BonusBox = (props: { ) : ( <> )} - - - + + + {props.amount} @@ -411,7 +411,12 @@ const RechargeModal = forwardRef( return ( - + {!detail ? ( complete === 0 ? ( <> @@ -424,7 +429,7 @@ const RechargeModal = forwardRef( fontSize={'16px'} borderColor={'grayModern.100'} > - {t('Recharge Amount')} + {t('credit_purchase')} - - {t('Balance')} + + {t('remaining_balance')} - - + + {formatMoney(balance).toFixed(2)} - + {t('Select Amount')} - - - - {t('first_recharge_title')}! - - {t('first_recharge_tips')}}> - - - + {specialBonus && specialBonus.length > 0 && ( + + + + {t('first_recharge_title')} + + + {t('first_recharge_tips')} + + } + > + + + + )} {steps.map((amount, index) => ( @@ -484,20 +501,21 @@ const RechargeModal = forwardRef( - - {t('Recharge Amount')} + + {t('custom_amount')} - - + + @@ -530,21 +548,25 @@ const RechargeModal = forwardRef( - - {t('Bonus')} {getBonus(amount)} - - + + + {t('Bonus')} + + + + {getBonus(amount)} + {t(item.title)}: - + {formatMoney(item.value).toFixed(2)} ))} diff --git a/frontend/providers/costcenter/src/components/cost_overview/components/pieChart.tsx b/frontend/providers/costcenter/src/components/cost_overview/components/pieChart.tsx index 47face06b72..65f11b02577 100644 --- a/frontend/providers/costcenter/src/components/cost_overview/components/pieChart.tsx +++ b/frontend/providers/costcenter/src/components/cost_overview/components/pieChart.tsx @@ -1,4 +1,5 @@ import { resourceType } from '@/constants/billing'; +import useEnvStore from '@/stores/env'; import { formatMoney } from '@/utils/format'; import ReactEChartsCore from 'echarts-for-react/lib/core'; import { PieChart } from 'echarts/charts'; @@ -20,9 +21,10 @@ echarts.use([ export default function CostChart({ data }: { data: number[]; appName: string }) { const { t } = useTranslation(); - + const { gpuEnabled } = useEnvStore(); const radius = ['50%', '90%']; - const result = [0, 1, 2, 3, 4].map((_, i) => { + + const result = (gpuEnabled ? [0, 1, 2, 3, 4, 5] : [0, 1, 2, 3, 4]).map((_, i) => { return [t(resourceType[i]), formatMoney(data[i]).toFixed(2)]; }); const title = t('All APP', { ns: 'applist' }) + '\n' + t('Cost Form'); diff --git a/frontend/providers/costcenter/src/components/cost_overview/cost.tsx b/frontend/providers/costcenter/src/components/cost_overview/cost.tsx index b115de9f1fb..6fc31516d04 100644 --- a/frontend/providers/costcenter/src/components/cost_overview/cost.tsx +++ b/frontend/providers/costcenter/src/components/cost_overview/cost.tsx @@ -1,15 +1,15 @@ -import { Box, Flex, HStack, Text } from '@chakra-ui/react'; -import { useTranslation } from 'next-i18next'; -import dynamic from 'next/dynamic'; import Notfound from '@/components/notFound'; -import { useQuery } from '@tanstack/react-query'; -import useOverviewStore from '@/stores/overview'; import request from '@/service/request'; -import { ApiResp, PropertiesCost } from '@/types'; import useBillingStore from '@/stores/billing'; +import useOverviewStore from '@/stores/overview'; +import { PropertiesCost } from '@/types'; +import { Box, Flex, HStack, Text } from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; +import { useTranslation } from 'next-i18next'; +import dynamic from 'next/dynamic'; +import SelectRange from '../billing/selectDateRange'; import AppNameMenu from '../menu/AppNameMenu'; import AppTypeMenu from '../menu/AppTypeMenu'; -import SelectRange from '../billing/selectDateRange'; const Chart = dynamic(() => import('./components/pieChart'), { ssr: false }); @@ -18,7 +18,6 @@ export const Cost = function Cost() { const startTime = useOverviewStore((state) => state.startTime); const endTime = useOverviewStore((state) => state.endTime); const { getNamespace, getAppName, getAppType, getRegion } = useBillingStore(); - const query = { namespace: getNamespace()?.[0] || '', appType: getAppType(), @@ -30,22 +29,11 @@ export const Cost = function Cost() { const { data, isInitialLoading, isFetching } = useQuery({ queryKey: ['billing', 'properties', 'costs', query], queryFn: () => { - return request.post>('/api/billing/costDistrube', query); + return request.post('/api/billing/costDistrube', query); }, select(data) { const _data = data.data; - return [ - // @ts-ignore - _data['0'], - // @ts-ignore - _data['1'], - // @ts-ignore - _data['2'], - // @ts-ignore - _data['3'], - // @ts-ignore - _data['4'] - ]; + return [_data['0'], _data['1'], _data['2'], _data['3'], _data['4'], _data['5']]; } }); return ( @@ -83,7 +71,7 @@ export const Cost = function Cost() { ) : ( - + )} ); diff --git a/frontend/providers/costcenter/src/components/outerLink.tsx b/frontend/providers/costcenter/src/components/outerLink.tsx index fbd7d41fdec..f7467117338 100644 --- a/frontend/providers/costcenter/src/components/outerLink.tsx +++ b/frontend/providers/costcenter/src/components/outerLink.tsx @@ -1,17 +1,18 @@ +import uil_info_circle from '@/assert/uil_info-circle.svg'; import { Flex, Img, Link } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; -import uil_info_circle from '@/assert/uil_info-circle.svg'; export default function Index({ text, href }: { text: string; href?: string }) { const { t } = useTranslation(); return ( - + s.currency); - + const { currency, gpuEnabled } = useEnvStore((s) => s); const columns = useMemo(() => { const columnHelper = createColumnHelper(); const customTh = (needCurrency?: boolean) => @@ -56,12 +55,14 @@ export function BillingDetailsTable({ function CustomCell(props: CellContext) { return ; }; - const getUnit = (x: string) => { + const getUnit = ( + x: 'cpu' | 'memory' | 'storage' | 'gpu' | 'network' | 'services.nodeports' + ) => { return function CustomCell(props: CellContext) { const resourceEntity = valuationMap.get(x); if (!resourceEntity) return '0'; const unit = resourceEntity.unit; - return props.cell.getValue() / resourceEntity.scale + ' ' + unit; + return props.cell.getValue() / resourceEntity.scale + ' ' + t(unit, { ns: 'common' }); }; }; return [ @@ -138,6 +139,20 @@ export function BillingDetailsTable({ header: customTh(), cell: customCell() }), + ...(gpuEnabled + ? [ + columnHelper.accessor((row) => row.used[5], { + id: TableHeaderID.GPU, + header: customTh(), + cell: getUnit('gpu') + }), + columnHelper.accessor((row) => row.used_amount[5], { + id: TableHeaderID.GPUAmount, + header: customTh(), + cell: customCell() + }) + ] + : []), columnHelper.accessor((row) => row.time, { id: TableHeaderID.TransactionTime, header: customTh(), diff --git a/frontend/providers/costcenter/src/components/valuation/CalculatorPanel/index.tsx b/frontend/providers/costcenter/src/components/valuation/CalculatorPanel/index.tsx index f23cc10285c..4a4f4029ef7 100644 --- a/frontend/providers/costcenter/src/components/valuation/CalculatorPanel/index.tsx +++ b/frontend/providers/costcenter/src/components/valuation/CalculatorPanel/index.tsx @@ -173,7 +173,7 @@ export default function CalculatorPanel({ }); } - const gpuData = priceData.filter((x) => x.title.startsWith('gpu-')); + const gpuData = priceData.filter((x) => x.isGpu); const totalAmount = useMemo(() => { const cpuPrice = priceData.find((x) => x.title === 'cpu')?.price || 0; const cpuAmount = config.resources.cpu.val * cpuPrice; @@ -335,54 +335,56 @@ export default function CalculatorPanel({ {/*gpu*/} - - - - - {t('GPU')} - - - - x.title)} - triggerRender={({ text, idx }) => { - const Icon = gpuData[idx].icon; - return ( - - - {text} - - ); - }} - itemRender={({ text, idx }) => { - const Icon = gpuData[idx].icon; - return ( - - - {text} - - ); - }} - > - { - if (count < 0) return; - updateGpuCount(count); - }} - min={0} - /> - - + {gpuEnabled && ( + + + + + {t('GPU')} + + + + x.title)} + triggerRender={({ text, idx }) => { + const Icon = gpuData[idx].icon; + return ( + + + {text} + + ); + }} + itemRender={({ text, idx }) => { + const Icon = gpuData[idx].icon; + return ( + + + {text} + + ); + }} + > + { + if (count < 0) return; + updateGpuCount(count); + }} + min={0} + /> + + + )} diff --git a/frontend/providers/costcenter/src/components/valuation/PriceTablePanel.tsx b/frontend/providers/costcenter/src/components/valuation/PriceTablePanel.tsx index f834077d1b0..94308de4b74 100644 --- a/frontend/providers/costcenter/src/components/valuation/PriceTablePanel.tsx +++ b/frontend/providers/costcenter/src/components/valuation/PriceTablePanel.tsx @@ -7,9 +7,11 @@ import { PricePayload, PriceTable } from '../table/PriceTable'; export function PriceTablePanel({ priceData }: { priceData: PricePayload[] }) { const { t } = useTranslation(); const gpuEnabled = useEnvStore((state) => state.gpuEnabled); - const baseData = priceData.filter((x) => x.title !== 'network' && !x.title.startsWith('gpu-')); - const networkData = priceData.filter((x) => x.title === 'network'); - const gpuData = priceData.filter((x) => x.title.startsWith('gpu-')); + const gpuData = priceData.filter((x) => x.isGpu); + const otherData = priceData.filter((x) => !x.isGpu); + const networkData = otherData.filter((x) => x.title === 'network'); + const baseData = otherData.filter((x) => x.title !== 'network'); + return ( diff --git a/frontend/providers/costcenter/src/components/valuation/quota.tsx b/frontend/providers/costcenter/src/components/valuation/quota.tsx index 13648840f83..445f70897d1 100644 --- a/frontend/providers/costcenter/src/components/valuation/quota.tsx +++ b/frontend/providers/costcenter/src/components/valuation/quota.tsx @@ -1,12 +1,14 @@ import { valuationMap } from '@/constants/payment'; import { UserQuotaItemType } from '@/pages/api/getQuota'; import request from '@/service/request'; +import useEnvStore from '@/stores/env'; import { ApiResp } from '@/types'; import { Box, Divider, HStack, Stack, StackProps, Text } from '@chakra-ui/react'; import { useQuery } from '@tanstack/react-query'; import dynamic from 'next/dynamic'; import { useTranslation } from 'react-i18next'; import CpuIcon from '../icons/CpuIcon'; +import GpuIcon from '../icons/GpuIcon'; import { MemoryIcon } from '../icons/MemoryIcon'; import { StorageIcon } from '../icons/StorageIcon'; const QuotaPie = dynamic(() => import('../cost_overview/components/quotaPieChart'), { ssr: false }); @@ -15,20 +17,27 @@ export default function Quota(props: StackProps) { const { data } = useQuery(['quota'], () => request>('/api/getQuota') ); + const { gpuEnabled } = useEnvStore(); const quota = (data?.data?.quota || []) - .filter((d) => d.type !== 'gpu') - .map((d) => { + .filter((d) => gpuEnabled || d.type !== 'gpu') + .flatMap((d) => { + const entity = valuationMap.get(d.type); + if (!entity) { + return []; + } const _limit = Number.parseInt(d.limit * 1000 + ''); const _used = Number.parseInt(d.used * 1000 + ''); - return { - ...d, - limit: _limit / 1000, - used: _used / 1000, - remain: (_limit - _used) / 1000, - title: t(d.type), - unit: valuationMap.get(d.type)?.unit, - bg: valuationMap.get(d.type)?.bg - }; + return [ + { + ...d, + limit: _limit / 1000, + used: _used / 1000, + remain: (_limit - _used) / 1000, + title: t(d.type), + unit: t(entity.unit), + bg: entity.bg + } + ]; }); return ( @@ -43,6 +52,8 @@ export default function Quota(props: StackProps) { ) : item.type === 'storage' ? ( + ) : item.type === 'gpu' ? ( + ) : ( <> )} diff --git a/frontend/providers/costcenter/src/constants/billing.ts b/frontend/providers/costcenter/src/constants/billing.ts index 84b81276f8c..474d76816b5 100644 --- a/frontend/providers/costcenter/src/constants/billing.ts +++ b/frontend/providers/costcenter/src/constants/billing.ts @@ -49,7 +49,8 @@ export enum TableHeaderID { 'TraderID' = 'Trader ID', 'Status' = 'Invoice Status', 'InvoiceCreateTime' = 'Invoice Create Time', - 'InvoiceUpdateTime' = 'Invoice Update Time' + 'InvoiceUpdateTime' = 'Invoice Update Time', + 'GPUAmount' = 'GPU Amount' } -export const resourceType = ['cpu', 'memory', 'storage', 'network', 'nodeports'] as const; +export const resourceType = ['cpu', 'memory', 'storage', 'network', 'nodeports', 'gpu'] as const; diff --git a/frontend/providers/costcenter/src/constants/payment.ts b/frontend/providers/costcenter/src/constants/payment.ts index 3b27de63f8d..316c30034ba 100644 --- a/frontend/providers/costcenter/src/constants/payment.ts +++ b/frontend/providers/costcenter/src/constants/payment.ts @@ -94,7 +94,7 @@ export const valuationMap = new Map([ ['gpu', { unit: 'GPU Unit', scale: 1000, bg: '#6FCA88', idx: 3 }], ['network', { unit: 'M', scale: 1, bg: '#F182AA', idx: 4 }], ['services.nodeports', { unit: 'port_unit', scale: 1000, bg: '#F182AA', idx: 5 }] -]); +] as const); // export const BillingUnitMap = new Map([ // ['cpu', { unit: ''}] // ['port', { unit: ''}] diff --git a/frontend/providers/costcenter/src/pages/resource_analysis/index.tsx b/frontend/providers/costcenter/src/pages/resource_analysis/index.tsx index 7eb5c8bf524..240e7e73e24 100644 --- a/frontend/providers/costcenter/src/pages/resource_analysis/index.tsx +++ b/frontend/providers/costcenter/src/pages/resource_analysis/index.tsx @@ -12,7 +12,7 @@ import { Box, Flex, Heading, HStack, Img, Stack, Text, VStack } from '@chakra-ui import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; -import { useMemo, useState } from 'react'; +import { useState } from 'react'; export default function Resource() { const { t } = useTranslation(); @@ -68,7 +68,6 @@ export default function Resource() { keepPreviousData: true, queryKey: ['appOverviewBilling', queryBody, page, pageSize] }); - const appOverviews = useMemo(() => data?.data?.overviews || [], [data?.data?.overviews]); return ( diff --git a/frontend/providers/costcenter/src/pages/valuation/index.tsx b/frontend/providers/costcenter/src/pages/valuation/index.tsx index e4798028443..e8b283e6716 100644 --- a/frontend/providers/costcenter/src/pages/valuation/index.tsx +++ b/frontend/providers/costcenter/src/pages/valuation/index.tsx @@ -26,6 +26,7 @@ type CardItem = { unit: string; idx: number; // used to sort icon: typeof Img; + isGpu: boolean; }; // 1 ,24, export const PRICE_CYCLE_SCALE = [1, 24, 168, 720, 8760]; @@ -49,7 +50,7 @@ function Valuation() { _data?.data?.properties // ?.filter((x) => !x.name.startsWith('gpu-')) ?.flatMap((x) => { - let props = valuationMap.get(x.name); + let props = valuationMap.get(x.name as any); if (!props) { if (!x.name.startsWith('gpu-')) return []; const gpuprops = valuationMap.get('gpu'); @@ -57,6 +58,7 @@ function Valuation() { props = gpuprops; } let icon: typeof Img; + let isGpu = x.name.startsWith('gpu-'); let title = x.name; let unit = [t(props.unit), t(CYCLE[cycleIdx])].join('/'); if (x.name === 'cpu') icon = CpuIcon; @@ -69,18 +71,18 @@ function Valuation() { icon = PortIcon; title = 'Port'; } else if (x.name.startsWith('gpu-')) { - // const reg = /nvidia/g icon = NvidiaIcon; + x.alias && (title = x.alias); } else return []; const price = (x.unit_price || 0) * (props.scale || 1); - console.log(x.name, x.unit_price, price); return [ { title, price, unit, idx: props.idx, - icon + icon, + isGpu } ]; }) @@ -113,8 +115,6 @@ function Valuation() { 、 - GET('/api/launchpad/getApps').then((res) => res.map(adaptAppListItem)); +export const getMyApps = () => GET('/api/launchpad/getApps'); export const getMyServiceAccount = () => GET('/api/launchpad/getServiceAccount').then((res) => diff --git a/frontend/providers/cronjob/src/constants/job.ts b/frontend/providers/cronjob/src/constants/job.ts index ed1f6bdff03..39ce6c3184c 100644 --- a/frontend/providers/cronjob/src/constants/job.ts +++ b/frontend/providers/cronjob/src/constants/job.ts @@ -163,6 +163,7 @@ export const DefaultJobEditValue: CronJobEditType = { launchpadName: '', launchpadId: '', serviceAccountName: '', + launchpadKind: 'Deployment', status: CronJobStatusMap['Running'], isPause: false, creatTime: '', diff --git a/frontend/providers/cronjob/src/pages/api/launchpad/getApps.ts b/frontend/providers/cronjob/src/pages/api/launchpad/getApps.ts index 099e0c92c51..6dbbc80d85b 100644 --- a/frontend/providers/cronjob/src/pages/api/launchpad/getApps.ts +++ b/frontend/providers/cronjob/src/pages/api/launchpad/getApps.ts @@ -3,6 +3,7 @@ import { ApiResp } from '@/services/kubernet'; import { authSession } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; +import { adaptAppListItem } from '@/utils/adapt'; export const appDeployKey = 'cloud.sealos.io/app-deploy-manager'; @@ -33,11 +34,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const apps = response .filter((item) => item.status === 'fulfilled') - .map((item: any) => item?.value?.body?.items) + .map((item: any, index) => { + const items = item?.value?.body?.items || []; + return items.map((app: any) => ({ + ...app, + kind: index === 0 ? 'Deployment' : 'StatefulSet' + })); + }) .filter((item) => item) .flat(); - jsonRes(res, { data: apps }); + jsonRes(res, { data: apps.map(adaptAppListItem) }); } catch (err: any) { jsonRes(res, { code: 500, diff --git a/frontend/providers/cronjob/src/pages/job/detail/components/AppMainInfo.tsx b/frontend/providers/cronjob/src/pages/job/detail/components/AppMainInfo.tsx index b1194e07f51..d89555525c6 100644 --- a/frontend/providers/cronjob/src/pages/job/detail/components/AppMainInfo.tsx +++ b/frontend/providers/cronjob/src/pages/job/detail/components/AppMainInfo.tsx @@ -94,6 +94,8 @@ export default function AppBaseInfo({ ? '#33BABB' : jobItem.status === 'failed' ? '#FF8492' + : jobItem.status === 'active' + ? '#33BABB' : '#FF8492' }} > diff --git a/frontend/providers/cronjob/src/pages/job/edit/components/Form.tsx b/frontend/providers/cronjob/src/pages/job/edit/components/Form.tsx index 125d36d382c..49409055d28 100644 --- a/frontend/providers/cronjob/src/pages/job/edit/components/Form.tsx +++ b/frontend/providers/cronjob/src/pages/job/edit/components/Form.tsx @@ -436,6 +436,7 @@ const Form = ({ formHook }: { formHook: UseFormReturn }) = setValue('replicas', launchpad.replicas); setValue('cpu', launchpad.cpu); setValue('memory', launchpad.memory); + setValue('launchpadKind', launchpad.kind); }} /> )} diff --git a/frontend/providers/cronjob/src/types/app.d.ts b/frontend/providers/cronjob/src/types/app.d.ts index 4e8ee24c00c..f654e979c34 100644 --- a/frontend/providers/cronjob/src/types/app.d.ts +++ b/frontend/providers/cronjob/src/types/app.d.ts @@ -6,4 +6,5 @@ export interface AppListItemType { cpu: number; memory: number; replicas: number; + kind: string; } diff --git a/frontend/providers/cronjob/src/types/job.d.ts b/frontend/providers/cronjob/src/types/job.d.ts index 81272b1b364..701053cadba 100644 --- a/frontend/providers/cronjob/src/types/job.d.ts +++ b/frontend/providers/cronjob/src/types/job.d.ts @@ -60,6 +60,7 @@ export type CronJobEditType = { launchpadName: string; launchpadId: string; serviceAccountName: string; + launchpadKind: string; } & CronJobDetailType; export type CronJobDetailType = { @@ -115,4 +116,5 @@ export type CronJobAnnotations = { launchpadName: string; launchpadId: string; replicas: string; + launchpadKind: string; }; diff --git a/frontend/providers/cronjob/src/utils/adapt.ts b/frontend/providers/cronjob/src/utils/adapt.ts index 3469f3055e2..865a5385786 100644 --- a/frontend/providers/cronjob/src/utils/adapt.ts +++ b/frontend/providers/cronjob/src/utils/adapt.ts @@ -15,7 +15,8 @@ import { V1Deployment, V1Job, V1Pod, - V1ServiceAccount + V1ServiceAccount, + V1StatefulSet } from '@kubernetes/client-node'; import dayjs from 'dayjs'; import cronstrue from 'cronstrue'; @@ -56,8 +57,16 @@ export const adaptCronJobDetail = async (job: V1CronJob): Promise { const commands = job.spec?.jobTemplate?.spec?.template?.spec?.containers?.[0]?.args; @@ -101,6 +110,7 @@ export const adaptCronJobDetail = async (job: V1CronJob): Promise { +export const adaptAppListItem = (app: V1Deployment | V1StatefulSet): AppListItemType => { return { id: app.metadata?.uid || ``, name: app.metadata?.name || 'app name', @@ -140,7 +150,8 @@ export const adaptAppListItem = (app: V1Deployment): AppListItemType => { memory: memoryFormatToMi( app.spec?.template?.spec?.containers?.[0]?.resources?.limits?.memory || '0' ), - replicas: app.spec?.replicas || 0 + replicas: app.spec?.replicas || 0, + kind: app.kind || '' }; }; diff --git a/frontend/providers/cronjob/src/utils/json2Yaml.ts b/frontend/providers/cronjob/src/utils/json2Yaml.ts index 191c8b4c400..5191bc741a4 100644 --- a/frontend/providers/cronjob/src/utils/json2Yaml.ts +++ b/frontend/providers/cronjob/src/utils/json2Yaml.ts @@ -56,7 +56,7 @@ export const json2CronJob = (data: CronJobEditType) => { command += ` && curl -k -X POST -H "Authorization: $(cat ~/.kube/config | jq -sRr @uri)" -d "appName=${data.launchpadName}&replica=${data.replicas}" https://${applaunchpadUrl}/api/v1alpha/updateReplica`; } if (data.enableResources) { - command += ` && kubectl set resources deployment ${data.launchpadName} --limits=cpu=${resources.limits.cpu},memory=${resources.limits.memory} --requests=cpu=${resources.requests.cpu},memory=${resources.requests.memory}`; + command += ` && kubectl set resources ${data.launchpadKind} ${data.launchpadName} --limits=cpu=${resources.limits.cpu},memory=${resources.limits.memory} --requests=cpu=${resources.requests.cpu},memory=${resources.requests.memory}`; } return command.replace(/\n/g, '') || ''; @@ -70,7 +70,8 @@ export const json2CronJob = (data: CronJobEditType) => { memory: `${str2Num(data.memory)}Mi`, launchpadName: data.launchpadName, launchpadId: data.launchpadId, - replicas: `${data.replicas}` + replicas: `${data.replicas}`, + launchpadKind: data.launchpadKind }; } diff --git a/frontend/providers/dbprovider/public/locales/en/common.json b/frontend/providers/dbprovider/public/locales/en/common.json index 373942dc3d8..e3215760850 100644 --- a/frontend/providers/dbprovider/public/locales/en/common.json +++ b/frontend/providers/dbprovider/public/locales/en/common.json @@ -7,8 +7,8 @@ "Continue": "Continue", "Creating": "Creating", "CronExpression": "Cycle Interval", - "DBList": "DataBases", - "DataBase": "DataBase", + "DBList": "Database", + "DataBase": "Database", "Day": "Day", "Delete": "Delete", "Deleting": "Deleting...", @@ -66,16 +66,18 @@ "storage_exceeds_quota": "Storage requested exceeds quota. Contact admin." }, "app_store": "App Store", - "application_source": "Application Source", + "application_source": "Source", "are_you_sure_to_perform_database_migration": "Confirm database migration?", "are_you_sure_you_want_to_turn_off_automatic_backup": "Confirm disabling auto backup?", "auto_backup": "Automated Backup", "automatic_backup_is_turned_off": "Auto backup disabled", + "backup_center": "Backup Center", + "backup_center_search_tip": "Search backup name, notes", "backup_completed": "Backup completed", "backup_database": "Backup Database", "backup_deleting": "Purging Backup", "backup_failed": "Backup Failed", - "backup_list": "Data Backups", + "backup_list": "Backups", "backup_name": "Backup Name", "backup_name_cannot_empty": "Must provide backup name", "backup_processing": "Saving Backup", @@ -121,6 +123,7 @@ "create_db": "Create Database", "creation_time": "Creation Time", "current_connections": "Current Connections", + "data_import": "Data Import", "data_migration_config": "Data Migration Settings", "database_config": "parameters", "database_edit_config": "Update Parameters", @@ -142,7 +145,7 @@ "db_name": "DataBase Name", "db_table": "DataBase Table", "dbconfig": { - "change_history": "Modification history", + "change_history": "Operation History", "commit": "submit", "confirm_updates": "Please confirm the parameters you modified:", "get_config_err": "Failed to obtain configuration file", @@ -150,7 +153,7 @@ "modify_time": "Modify Time", "no_changes": "No modification yet", "original_value": "Original value", - "parameter": "Parameter Config", + "parameter": "Parameters", "parameter_name": "parameter name", "parameter_value": "Parameter value", "prompt": "Change notice", @@ -161,6 +164,8 @@ }, "delete_anyway": "Force Delete", "delete_backup": "Delete Backup", + "delete_backup_with_db": "Keep Backups", + "delete_backup_with_db_tip": "Delete the databases but leave the backups as they are", "delete_failed": "Failed to delete", "delete_hint": "Warning: This will permanently delete all data in the database. Confirm to proceed.", "delete_sealaf_app_tip": "The database is deployed via cloud development. \nSimply deleting the database does not clear out all components of your app, which may still incur charges. \nTo completely uninstall the app and clean all related components, uninstall the entire app in cloud development.", @@ -178,7 +183,7 @@ "enable_external_network_access": "Allow public network access", "enter_save": "Press Enter to save. 'All' exports the entire database.", "error_log": { - "analysis": "Log Analysis", + "analysis": "Logs", "collection_time": "Collection Time", "content": "Information", "error_log": "Error Log", @@ -204,7 +209,7 @@ "have_error": "Failed", "hits_ratio": "Hits Ratio", "hour": "hour", - "import_through_file": "Import via File", + "import_through_file": "File Import", "important_tips_for_migrating": "Tip: Create a new database in sink DB if source_database and sink_database have overlapping data, to avoid conflicts", "innodb_buffer_pool": "InnoDB Buffer Pool", "intranet_address": "Private Address", @@ -215,7 +220,7 @@ "limit_cpu": "CPU Limit", "limit_memory": "Memory Limit", "lost_file": "File Missing", - "manage_all_resources": "Manage all resources", + "manage_all_resources": "Manage", "manual_backup": "Manual Backup", "manual_backup_tip": "Tip: Backup during off-peak hours. Avoid DDL operations to prevent locking. Be patient if data size is large. Backup starts 1 min after confirmation.", "max_replicas": "Max Replicas: ", @@ -255,8 +260,9 @@ "no_data_available": "No Data Available", "no_logs_for_now": "No logs for now", "not_allow_standalone_use": "This application is not allowed to be used alone. Click OK to go to Sealos Desktop for use.", - "online_import": "Import Online", + "online_import": "Online Import", "operation": "Operation", + "overview": "Overview", "page_faults": "Page Faults", "pause_error": "Failed to pause the database", "pause_hint": "Pausing the service will stop the calculation of charges for CPU and memory, but charges for storage and external network ports will still apply. Would you like to pause now?", @@ -318,13 +324,18 @@ "upload_dump_file": "Upload Dump File", "use_docs": "Documentation", "version": "Version", + "wipeout_backup_with_db": "Discard Backups", + "wipeout_backup_with_db_tip": "Delete the databases and the backups", "within_1_day": "Within 1 day", "within_1_hour": "Within 1 hour", "within_5_minutes": "Within 5 minutes", "yaml_file": "YAML", "you_have_successfully_deployed_database": "You have successfully deployed and created a database!", - "delete_backup_with_db": "Keep Backups", - "delete_backup_with_db_tip": "Delete the databases but leave the backups as they are", - "wipeout_backup_with_db": "Discard Backups", - "wipeout_backup_with_db_tip": "Delete the databases and the backups" + "change_log": "History", + "VerticalScaling": "Vertical Scaling", + "VerticalScalingCPU": "Vertical Scaling (CPU)", + "VerticalScalingMemory": "Vertical Scaling (Memory)", + "HorizontalScaling": "Horizontal Scaling (Instance)", + "VolumeExpansion": "Volume Expansion (Disk)", + "Stop": "Stop" } \ No newline at end of file diff --git a/frontend/providers/dbprovider/public/locales/zh/common.json b/frontend/providers/dbprovider/public/locales/zh/common.json index 7b6056a88cc..de1eddc6060 100644 --- a/frontend/providers/dbprovider/public/locales/zh/common.json +++ b/frontend/providers/dbprovider/public/locales/zh/common.json @@ -66,11 +66,13 @@ "storage_exceeds_quota": "申请的 '存储' 超出限制,请联系管理员" }, "app_store": "应用商店", - "application_source": "应用来源", + "application_source": "来源", "are_you_sure_to_perform_database_migration": "确定执行数据库迁移吗?", "are_you_sure_you_want_to_turn_off_automatic_backup": "确定关闭自动备份吗", "auto_backup": "自动备份", "automatic_backup_is_turned_off": "已关闭自动备份", + "backup_center": "备份中心", + "backup_center_search_tip": "搜索备份名称、备注", "backup_completed": "备份成功", "backup_database": "备份数据库", "backup_deleting": "删除中", @@ -84,7 +86,7 @@ "backup_success_tip": "备份任务已经成功创建", "backup_time": "备份时间", "balance": "余额", - "basic": "基础配置", + "basic": "基础信息", "billing_standards": "计费标准", "block_read_time": "读数据块时间", "block_write_time": "写数据块时间", @@ -118,9 +120,10 @@ "copy_success": "复制成功", "covering_risks": "覆盖风险", "cpu": "CPU", - "create_db": "新建数据库", + "create_db": "新建", "creation_time": "创建时间", "current_connections": "当前连接数", + "data_import": "数据导入", "data_migration_config": "数据迁移配置", "database_config": "数据库参数", "database_edit_config": "变更参数", @@ -142,7 +145,7 @@ "db_name": "数据库名字", "db_table": "数据库表", "dbconfig": { - "change_history": "修改历史", + "change_history": "变更历史", "commit": "提交", "confirm_updates": "请确认您修改的参数:", "get_config_err": "获取配置文件失败", @@ -161,6 +164,8 @@ }, "delete_anyway": "仍要删除", "delete_backup": "删除备份", + "delete_backup_with_db": "保留备份", + "delete_backup_with_db_tip": "在删除数据库时,保留其备份", "delete_failed": "删除出现意外", "delete_hint": "如果确认要删除这个数据库吗?如果执行此操作,将删除该数据库的所有数据。", "delete_sealaf_app_tip": "该数据库是通过云开发部署的。仅删除数据库无法清除应用的所有组件,这些组件可能仍会产生费用。要彻底卸载应用并清理所有相关组件,请在云开发中卸载整个应用。", @@ -258,6 +263,7 @@ "not_allow_standalone_use": "该应用不允许单独使用,点击确认前往 Sealos Desktop 使用。", "online_import": "在线导入", "operation": "操作", + "overview": "概览", "page_faults": "页错误", "pause_error": "数据库暂停失败", "pause_hint": "暂停服务将停止计算 CPU 和内存等费用,但存储和外网端口仍将产生费用。是否现在暂停?", @@ -319,13 +325,18 @@ "upload_dump_file": "点击上传 Dump 文件", "use_docs": "使用文档", "version": "版本", + "wipeout_backup_with_db": "随数据库删除", + "wipeout_backup_with_db_tip": "在删除数据库时,删除其备份", "within_1_day": "一天内", "within_1_hour": "一小时内", "within_5_minutes": "五分钟内", "yaml_file": "YAML 文件", "you_have_successfully_deployed_database": "您已成功部署创建一个数据库!", - "delete_backup_with_db": "保留备份", - "delete_backup_with_db_tip": "在删除数据库时,保留其备份", - "wipeout_backup_with_db": "随数据库删除", - "wipeout_backup_with_db_tip": "在删除数据库时,删除其备份" + "change_log": "变更历史", + "VerticalScaling": "纵向扩展", + "VerticalScalingCPU": "纵向扩展(CPU)", + "VerticalScalingMemory": "纵向扩展(内存)", + "HorizontalScaling": "横向扩展(实例数)", + "VolumeExpansion": "卷扩展(磁盘)", + "Stop": "停止" } \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/api/backup.ts b/frontend/providers/dbprovider/src/api/backup.ts index 70b8f14d151..79bdda6b51b 100644 --- a/frontend/providers/dbprovider/src/api/backup.ts +++ b/frontend/providers/dbprovider/src/api/backup.ts @@ -3,12 +3,15 @@ import type { Props as CreateBackupPros } from '@/pages/api/backup/create'; import { adaptBackup, adaptBackupByCluster, adaptDBDetail } from '@/utils/adapt'; import { AutoBackupFormType } from '@/types/backup'; import type { Props as UpdatePolicyProps } from '@/pages/api/backup/updatePolicy'; +import { BackupItemType } from '@/types/db'; export const createBackup = (data: CreateBackupPros) => POST('/api/backup/create', data); export const getBackupList = (dbName: string) => GET('/api/backup/getBackupList', { dbName }).then((res) => res.map(adaptBackup)); +export const getBackups = () => GET('/api/backup/getBackups'); + export const deleteBackup = (backupName: string) => DELETE(`/api/backup/delBackup?backupName=${backupName}`); diff --git a/frontend/providers/dbprovider/src/api/db.ts b/frontend/providers/dbprovider/src/api/db.ts index 1ff7eb6d159..267c05ec700 100644 --- a/frontend/providers/dbprovider/src/api/db.ts +++ b/frontend/providers/dbprovider/src/api/db.ts @@ -12,17 +12,17 @@ import type { import { LogTypeEnum } from '@/constants/log'; import { MonitorChartDataResult } from '@/types/monitor'; import { adaptDBDetail, adaptDBListItem, adaptEvents, adaptPod } from '@/utils/adapt'; -import { json2Restart } from '@/utils/json2Yaml'; +import { json2BasicOps } from '@/utils/json2Yaml'; import { TFile } from '@/utils/kubeFileSystem'; import { LogResult } from '@/utils/logParsers/LogParser'; import { V1Service, V1StatefulSet } from '@kubernetes/client-node'; -import { json2StartOrStop } from '../utils/json2Yaml'; +import { AxiosRequestConfig } from 'axios'; export const getMyDBList = () => GET('/api/getDBList').then((data) => data.map(adaptDBListItem)); -export const getDBByName = (name: string) => - GET(`/api/getDBByName?name=${name}`).then(adaptDBDetail); +export const getDBByName = (name: string, config?: AxiosRequestConfig) => + GET(`/api/getDBByName?name=${name}`, {}, config).then(adaptDBDetail); export const getConfigByName = ({ name, dbType }: { name: string; dbType: DBType }) => GET(`/api/getConfigByName?name=${name}&dbType=${dbType}`); @@ -62,7 +62,7 @@ export const restartPodByName = (podName: string) => GET(`/api/pod/restartPod?po /* db operation */ export const restartDB = (data: { dbName: string; dbType: DBType }) => { - const yaml = json2Restart(data); + const yaml = json2BasicOps({ ...data, type: 'Restart' }); return applyYamlList([yaml], 'update'); }; @@ -106,6 +106,12 @@ export const getOpsRequest = ({ dbType }); +export const getOperationLog = ({ name, dbType }: { name: string; dbType: DBType }) => + GET(`/api/opsrequest/operationlog`, { + name, + dbType + }); + export const getLogFiles = ({ podName, dbType, diff --git a/frontend/providers/dbprovider/src/components/BaseTable/baseTable.tsx b/frontend/providers/dbprovider/src/components/BaseTable/baseTable.tsx index fc3832a73f4..149a4ae402f 100644 --- a/frontend/providers/dbprovider/src/components/BaseTable/baseTable.tsx +++ b/frontend/providers/dbprovider/src/components/BaseTable/baseTable.tsx @@ -1,4 +1,5 @@ import { + HTMLChakraProps, Spinner, Table, TableContainer, @@ -9,17 +10,34 @@ import { Thead, Tr } from '@chakra-ui/react'; -import { Table as ReactTable, flexRender } from '@tanstack/react-table'; +import { Column, Table as ReactTable, flexRender } from '@tanstack/react-table'; +import { CSSProperties } from 'react'; + +const getCommonPinningStyles = (column: Column): CSSProperties => { + const isPinned = column.getIsPinned(); + + return { + position: isPinned ? 'sticky' : 'relative', + left: isPinned === 'left' ? 0 : undefined, + right: isPinned === 'right' ? 0 : undefined, + zIndex: isPinned ? 10 : 0 + }; +}; export function BaseTable({ table, isLoading, + tdStyle, ...props -}: { table: ReactTable; isLoading: boolean } & TableContainerProps) { +}: { + table: ReactTable; + isLoading: boolean; + tdStyle?: HTMLChakraProps<'td'>; +} & TableContainerProps) { return ( - + {table.getHeaderGroups().map((headers) => { return ( @@ -30,9 +48,16 @@ export function BaseTable({ py="13px" px={'24px'} key={header.id} - backgroundColor={'grayModern.50'} + bg={'grayModern.100'} color={'grayModern.600'} border={'none'} + _first={{ + borderLeftRadius: '6px' + }} + _last={{ + borderRightRadius: '6px' + }} + {...(getCommonPinningStyles(header.column) as HTMLChakraProps<'th'>)} > {flexRender(header.column.columnDef.header, header.getContext())} @@ -50,17 +75,23 @@ export function BaseTable({ ) : ( - table.getRowModel().rows.map((item) => { + table.getRowModel().rows.map((item, index) => { return ( - + {item.getAllCells().map((cell, i) => { + const isPinned = cell.column.getIsPinned(); return ( - ); diff --git a/frontend/providers/dbprovider/src/components/BaseTable/customMenu.tsx b/frontend/providers/dbprovider/src/components/BaseTable/customMenu.tsx new file mode 100644 index 00000000000..7a2d799efee --- /dev/null +++ b/frontend/providers/dbprovider/src/components/BaseTable/customMenu.tsx @@ -0,0 +1,97 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Box, Portal } from '@chakra-ui/react'; + +interface MenuItemProps { + isActive?: boolean; + child: React.ReactNode; + onClick: () => void; + menuItemStyle?: any; + isDisabled?: boolean; +} + +interface CustomMenuProps { + width: number; + Button: React.ReactNode; + menuList: MenuItemProps[]; +} + +export const CustomMenu = ({ width, Button, menuList }: CustomMenuProps) => { + const [isOpen, setIsOpen] = useState(false); + const buttonRef = useRef(null); + const menuRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + menuRef.current && + buttonRef.current && + !menuRef.current.contains(event.target as Node) && + !buttonRef.current.contains(event.target as Node) + ) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + const handleItemClick = (item: MenuItemProps) => { + if (!item.isDisabled) { + item.onClick(); + setIsOpen(false); + } + }; + + const defaultMenuItemStyles = { + borderRadius: '4px', + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + py: '6px', + px: '4px', + _hover: { + backgroundColor: 'rgba(17, 24, 36, 0.05)', + color: 'brightBlue.600' + } + }; + + return ( + + setIsOpen(!isOpen)}>{Button} + + {isOpen && ( + + + {menuList.map((item, i) => ( + handleItemClick(item)} + color={item.isActive ? 'hover.blue' : 'grayModern.600'} + opacity={item.isDisabled ? 0.5 : 1} + {...defaultMenuItemStyles} + {...item.menuItemStyle} + > + {item.child} + + ))} + + + )} + + ); +}; diff --git a/frontend/providers/dbprovider/src/components/DBStatusTag/index.tsx b/frontend/providers/dbprovider/src/components/DBStatusTag/index.tsx index c1e5ee9a957..f14da62a73c 100644 --- a/frontend/providers/dbprovider/src/components/DBStatusTag/index.tsx +++ b/frontend/providers/dbprovider/src/components/DBStatusTag/index.tsx @@ -43,6 +43,7 @@ const DBStatusTag = ({ fontWeight={'bold'} alignItems={'center'} minW={'88px'} + maxW={'110px'} whiteSpace={'nowrap'} > diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/analyze.svg b/frontend/providers/dbprovider/src/components/Icon/icons/analyze.svg index 50317864733..30ce76ff4ff 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/analyze.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/analyze.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/arrowLeft.svg b/frontend/providers/dbprovider/src/components/Icon/icons/arrowLeft.svg index 9e246b28759..13e1ec9d955 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/arrowLeft.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/arrowLeft.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/backup.svg b/frontend/providers/dbprovider/src/components/Icon/icons/backup.svg index 6bc2be8a53e..74220341b48 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/backup.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/backup.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/chevron-down.svg b/frontend/providers/dbprovider/src/components/Icon/icons/chevron-down.svg new file mode 100644 index 00000000000..5840e8c8c32 --- /dev/null +++ b/frontend/providers/dbprovider/src/components/Icon/icons/chevron-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/config.svg b/frontend/providers/dbprovider/src/components/Icon/icons/config.svg index aff15fbd251..37bc601a2f3 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/config.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/config.svg @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/copy.svg b/frontend/providers/dbprovider/src/components/Icon/icons/copy.svg index bfd38df8ede..f96a4564dcf 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/copy.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/copy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/file.svg b/frontend/providers/dbprovider/src/components/Icon/icons/file.svg index e24054c2dfc..b69a4ae8706 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/file.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/file.svg @@ -1,7 +1,7 @@ - + - - + + diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/import.svg b/frontend/providers/dbprovider/src/components/Icon/icons/import.svg index d57c59dccd2..890306b981c 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/import.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/import.svg @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/instance.svg b/frontend/providers/dbprovider/src/components/Icon/icons/instance.svg index 864e27c4cc4..531b58db3e5 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/instance.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/instance.svg @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/logo-linear.svg b/frontend/providers/dbprovider/src/components/Icon/icons/logo-linear.svg new file mode 100644 index 00000000000..1e7680f2129 --- /dev/null +++ b/frontend/providers/dbprovider/src/components/Icon/icons/logo-linear.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/logo.svg b/frontend/providers/dbprovider/src/components/Icon/icons/logo.svg index 9747eb120b2..81eb39fdde1 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/logo.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/logo.svg @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/monitor.svg b/frontend/providers/dbprovider/src/components/Icon/icons/monitor.svg index 185a68a8b10..44aaf7e7e41 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/monitor.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/monitor.svg @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/overview.svg b/frontend/providers/dbprovider/src/components/Icon/icons/overview.svg new file mode 100644 index 00000000000..3ff70d36016 --- /dev/null +++ b/frontend/providers/dbprovider/src/components/Icon/icons/overview.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/restore.svg b/frontend/providers/dbprovider/src/components/Icon/icons/restore.svg index 2e89f43b666..b37a437444a 100644 --- a/frontend/providers/dbprovider/src/components/Icon/icons/restore.svg +++ b/frontend/providers/dbprovider/src/components/Icon/icons/restore.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/components/Icon/index.tsx b/frontend/providers/dbprovider/src/components/Icon/index.tsx index b4b35735a65..12acf066437 100644 --- a/frontend/providers/dbprovider/src/components/Icon/index.tsx +++ b/frontend/providers/dbprovider/src/components/Icon/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type { IconProps } from '@chakra-ui/react'; import { Icon } from '@chakra-ui/react'; -const map = { +export const IconMap = { more: require('./icons/more.svg').default, podList: require('./icons/podList.svg').default, arrowLeft: require('./icons/arrowLeft.svg').default, @@ -55,8 +55,11 @@ const map = { config: require('./icons/config.svg').default, backupSettings: require('./icons/backupSettings.svg').default, monitor: require('./icons/monitor.svg').default, + logoLinear: require('./icons/logo-linear.svg').default, arrowDown: require('./icons/arrowDown.svg').default, - docs: require('./icons/docs.svg').default + docs: require('./icons/docs.svg').default, + chevronDown: require('./icons/chevron-down.svg').default, + overview: require('./icons/overview.svg').default }; const MyIcon = ({ @@ -64,9 +67,16 @@ const MyIcon = ({ w = 'auto', h = 'auto', ...props -}: { name: keyof typeof map } & IconProps) => { - return map[name] ? ( - +}: { name: keyof typeof IconMap } & IconProps) => { + return IconMap[name] ? ( + ) : null; }; diff --git a/frontend/providers/dbprovider/src/components/Sidebar/index.tsx b/frontend/providers/dbprovider/src/components/Sidebar/index.tsx new file mode 100644 index 00000000000..5facd2570ea --- /dev/null +++ b/frontend/providers/dbprovider/src/components/Sidebar/index.tsx @@ -0,0 +1,60 @@ +import { Center, Text, Stack } from '@chakra-ui/react'; +import MyIcon from '../Icon'; + +import { useTranslation } from 'next-i18next'; +import { useRouter } from 'next/router'; + +export default function Sidebar() { + const { t } = useTranslation(); + const router = useRouter(); + + const siderbarMap = [ + { + label: t('DataBase'), + icon: ( + + ), + path: '/dbs' + }, + { + label: t('Backup'), + icon: ( + + ), + path: '/backups' + } + ]; + + return ( + + {siderbarMap.map((item) => ( +
router.replace(item.path)} + > + {item.icon} + + {item.label} + +
+ ))} + + ); +} diff --git a/frontend/providers/dbprovider/src/constants/db.ts b/frontend/providers/dbprovider/src/constants/db.ts index aa1a527c74b..22d8849f550 100644 --- a/frontend/providers/dbprovider/src/constants/db.ts +++ b/frontend/providers/dbprovider/src/constants/db.ts @@ -20,6 +20,8 @@ export const templateDeployKey = 'cloud.sealos.io/deploy-on-sealos'; export const sealafDeployKey = 'sealaf-app'; export const DBReconfigureKey = 'ops.kubeblocks.io/ops-type=Reconfiguring'; +export const DBNameLabel = 'app.kubernetes.io/instance'; + export enum DBTypeEnum { postgresql = 'postgresql', mongodb = 'mongodb', @@ -196,7 +198,7 @@ export const podStatusMap = { [PodStatusEnum.running]: { label: 'running', value: PodStatusEnum.running, - bg: '#47C8BF' + bg: '#6CD99F' }, [PodStatusEnum.waiting]: { label: 'waiting', @@ -215,12 +217,12 @@ export const maxReplicasKey = 'deploy.cloud.sealos.io/maxReplicas'; export const minReplicasKey = 'deploy.cloud.sealos.io/minReplicas'; export const DBTypeList = [ - { id: DBTypeEnum.postgresql, label: 'postgres' }, - { id: DBTypeEnum.mongodb, label: 'mongo' }, - { id: DBTypeEnum.mysql, label: 'mysql' }, - { id: DBTypeEnum.redis, label: 'redis' }, - { id: DBTypeEnum.kafka, label: 'kafka' }, - { id: DBTypeEnum.milvus, label: 'milvus' } + { id: DBTypeEnum.postgresql, label: 'PostgreSQL' }, + { id: DBTypeEnum.mongodb, label: 'MongoDB' }, + { id: DBTypeEnum.mysql, label: 'MySQL' }, + { id: DBTypeEnum.redis, label: 'Redis' }, + { id: DBTypeEnum.kafka, label: 'Kafka' }, + { id: DBTypeEnum.milvus, label: 'Milvus' } // { id: DBTypeEnum.qdrant, label: 'qdrant' }, // { id: DBTypeEnum.nebula, label: 'nebula' }, // { id: DBTypeEnum.weaviate, label: 'weaviate' } diff --git a/frontend/providers/dbprovider/src/constants/theme.ts b/frontend/providers/dbprovider/src/constants/theme.ts index b8638768891..e955941b49e 100644 --- a/frontend/providers/dbprovider/src/constants/theme.ts +++ b/frontend/providers/dbprovider/src/constants/theme.ts @@ -38,9 +38,9 @@ export const theme = extendTheme(SealosTheme, { color: 'grayModern.900', fontSize: 'md', height: '100%', - overflowY: 'auto', - fontWeight: 400, - minWidth: '700px' + fontWeight: 400 + // overflowY: 'auto', + // minWidth: '700px' } } }, diff --git a/frontend/providers/dbprovider/src/pages/_app.tsx b/frontend/providers/dbprovider/src/pages/_app.tsx index 0969754d8c5..c2ff79ecc5f 100644 --- a/frontend/providers/dbprovider/src/pages/_app.tsx +++ b/frontend/providers/dbprovider/src/pages/_app.tsx @@ -97,7 +97,7 @@ function App({ Component, pageProps }: AppProps) { const changeI18n = async (data: any) => { const lastLang = getLangStore(); const newLang = data.currentLanguage; - if (lastLang !== newLang) { + if (lastLang !== newLang && typeof i18n?.changeLanguage === 'function') { i18n?.changeLanguage(newLang); setLangStore(newLang); setRefresh((state) => !state); @@ -146,7 +146,7 @@ function App({ Component, pageProps }: AppProps) { try { if (e.data?.type === 'InternalAppCall' && e.data?.name) { router.push({ - pathname: '/db/detail', + pathname: '/redirect', query: { name: e.data.name } diff --git a/frontend/providers/dbprovider/src/pages/api/backup/getBackupList.ts b/frontend/providers/dbprovider/src/pages/api/backup/getBackupList.ts index 5f0190fc54b..6c8ad383133 100644 --- a/frontend/providers/dbprovider/src/pages/api/backup/getBackupList.ts +++ b/frontend/providers/dbprovider/src/pages/api/backup/getBackupList.ts @@ -20,9 +20,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< return; } + const data = await getBackupListByDBName({ dbName, req }); try { jsonRes(res, { - data: await getBackups({ dbName, req }) + data }); } catch (err: any) { jsonRes(res, { @@ -32,7 +33,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< } } -export async function getBackups({ dbName, req }: Props & { req: NextApiRequest }) { +export async function getBackupListByDBName({ dbName, req }: Props & { req: NextApiRequest }) { const group = 'dataprotection.kubeblocks.io'; const version = 'v1alpha1'; const plural = 'backups'; diff --git a/frontend/providers/dbprovider/src/pages/api/backup/getBackups.ts b/frontend/providers/dbprovider/src/pages/api/backup/getBackups.ts new file mode 100644 index 00000000000..eda0218276d --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/api/backup/getBackups.ts @@ -0,0 +1,39 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { ApiResp } from '@/services/kubernet'; +import { authSession } from '@/services/backend/auth'; +import { getK8s } from '@/services/backend/kubernetes'; +import { jsonRes } from '@/services/backend/response'; +import { crLabelKey } from '@/constants/db'; +import { adaptBackup } from '@/utils/adapt'; + +export type Props = { + dbName: string; +}; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const group = 'dataprotection.kubeblocks.io'; + const version = 'v1alpha1'; + const plural = 'backups'; + + const { k8sCustomObjects, namespace } = await getK8s({ + kubeconfig: await authSession(req) + }); + + const { body } = (await k8sCustomObjects.listNamespacedCustomObject( + group, + version, + namespace, + plural + )) as { body: { items: any[] } }; + + try { + jsonRes(res, { + data: body.items.map(adaptBackup) + }); + } catch (err: any) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/frontend/providers/dbprovider/src/pages/api/createDB.ts b/frontend/providers/dbprovider/src/pages/api/createDB.ts index 7b94e2832ac..7fa85cf4ed8 100644 --- a/frontend/providers/dbprovider/src/pages/api/createDB.ts +++ b/frontend/providers/dbprovider/src/pages/api/createDB.ts @@ -4,11 +4,11 @@ import { jsonRes } from '@/services/backend/response'; import { ApiResp } from '@/services/kubernet'; import { KbPgClusterType } from '@/types/cluster'; import { BackupItemType, DBEditType } from '@/types/db'; -import { json2Account, json2ClusterOps, json2CreateCluster } from '@/utils/json2Yaml'; +import { json2Account, json2ResourceOps, json2CreateCluster } from '@/utils/json2Yaml'; import type { NextApiRequest, NextApiResponse } from 'next'; import { updateBackupPolicyApi } from './backup/updatePolicy'; import { BackupSupportedDBTypeList } from '@/constants/db'; -import { convertBackupFormToSpec } from '@/utils/adapt'; +import { adaptDBDetail, convertBackupFormToSpec } from '@/utils/adapt'; import { CustomObjectsApi, PatchUtils } from '@kubernetes/client-node'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -33,53 +33,38 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< )) as { body: KbPgClusterType; }; - - const currentConfig = { - cpu: parseInt(body.spec.componentSpecs[0].resources.limits.cpu.replace('m', '')), - memory: parseInt(body.spec.componentSpecs[0].resources.limits.memory.replace('Mi', '')), - replicas: body.spec.componentSpecs[0].replicas, - storage: parseInt( - body.spec.componentSpecs[0].volumeClaimTemplates[0].spec.resources.requests.storage.replace( - 'Gi', - '' - ) - ), - terminationPolicy: body.spec.terminationPolicy - }; + const { cpu, memory, replicas, storage, terminationPolicy } = adaptDBDetail(body); const opsRequests = []; - if (currentConfig.cpu !== dbForm.cpu || currentConfig.memory !== dbForm.memory) { - const verticalScalingYaml = json2ClusterOps(dbForm, 'VerticalScaling'); + if (cpu !== dbForm.cpu || memory !== dbForm.memory) { + const verticalScalingYaml = json2ResourceOps(dbForm, 'VerticalScaling'); opsRequests.push(verticalScalingYaml); } - if (currentConfig.replicas !== dbForm.replicas) { - const horizontalScalingYaml = json2ClusterOps(dbForm, 'HorizontalScaling'); + if (replicas !== dbForm.replicas) { + const horizontalScalingYaml = json2ResourceOps(dbForm, 'HorizontalScaling'); opsRequests.push(horizontalScalingYaml); } - if (dbForm.storage > currentConfig.storage) { - const volumeExpansionYaml = json2ClusterOps(dbForm, 'VolumeExpansion'); + if (dbForm.storage > storage) { + const volumeExpansionYaml = json2ResourceOps(dbForm, 'VolumeExpansion'); opsRequests.push(volumeExpansionYaml); } console.log('DB Edit Operation:', { dbName: dbForm.dbName, changes: { - cpu: currentConfig.cpu !== dbForm.cpu, - memory: currentConfig.memory !== dbForm.memory, - replicas: currentConfig.replicas !== dbForm.replicas, - storage: dbForm.storage > currentConfig.storage + cpu: cpu !== dbForm.cpu, + memory: memory !== dbForm.memory, + replicas: replicas !== dbForm.replicas, + storage: dbForm.storage > storage }, opsCount: opsRequests.length }); if (opsRequests.length > 0) { await applyYamlList(opsRequests, 'create'); - return jsonRes(res, { - data: `Successfully submitted ${opsRequests.length} change requests` - }); } if (BackupSupportedDBTypeList.includes(dbForm.dbType) && dbForm?.autoBackup) { @@ -96,7 +81,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< namespace }); - if (currentConfig.terminationPolicy !== dbForm.terminationPolicy) { + if (terminationPolicy !== dbForm.terminationPolicy) { await updateTerminationPolicyApi({ dbName: dbForm.dbName, terminationPolicy: dbForm.terminationPolicy, @@ -107,12 +92,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< } return jsonRes(res, { - data: 'success update db' + data: `Successfully submitted ${opsRequests.length} change requests` }); } const account = json2Account(dbForm); - const cluster = json2CreateCluster(dbForm, backupInfo); + const cluster = json2CreateCluster(dbForm, backupInfo, { + storageClassName: process.env.STORAGE_CLASSNAME + }); await applyYamlList([account, cluster], 'create'); const { body } = (await k8sCustomObjects.getNamespacedCustomObject( 'apps.kubeblocks.io', diff --git a/frontend/providers/dbprovider/src/pages/api/delDBByName.ts b/frontend/providers/dbprovider/src/pages/api/delDBByName.ts index cfe12dd74dc..073b98fb40e 100644 --- a/frontend/providers/dbprovider/src/pages/api/delDBByName.ts +++ b/frontend/providers/dbprovider/src/pages/api/delDBByName.ts @@ -3,7 +3,7 @@ import { ApiResp } from '@/services/kubernet'; import { authSession } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; -import { getBackups } from './backup/getBackupList'; +import { getBackupListByDBName } from './backup/getBackupList'; import { delBackupByName } from './backup/delBackup'; import { getMigrateList } from './migrate/list'; import { delMigrateByName } from './migrate/delete'; @@ -40,7 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< } // get backup and delete - const backups = await getBackups({ dbName: name, req }); + const backups = await getBackupListByDBName({ dbName: name, req }); await Promise.all( backups.map((item) => delBackupByName({ backupName: item.metadata.name, req })) ); diff --git a/frontend/providers/dbprovider/src/pages/api/getDBByName.ts b/frontend/providers/dbprovider/src/pages/api/getDBByName.ts index 42efd389183..72fbcd25737 100644 --- a/frontend/providers/dbprovider/src/pages/api/getDBByName.ts +++ b/frontend/providers/dbprovider/src/pages/api/getDBByName.ts @@ -3,6 +3,7 @@ import { ApiResp } from '@/services/kubernet'; import { authSession } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; +import { KbPgClusterType } from '@/types/cluster'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -29,13 +30,15 @@ export async function getCluster(req: NextApiRequest, name: string) { kubeconfig: await authSession(req) }); - const { body } = await k8sCustomObjects.getNamespacedCustomObject( + const { body } = (await k8sCustomObjects.getNamespacedCustomObject( 'apps.kubeblocks.io', 'v1alpha1', namespace, 'clusters', name - ); + )) as { + body: KbPgClusterType; + }; return body; } diff --git a/frontend/providers/dbprovider/src/pages/api/opsrequest/operationlog.ts b/frontend/providers/dbprovider/src/pages/api/opsrequest/operationlog.ts new file mode 100644 index 00000000000..bbab32af4dd --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/api/opsrequest/operationlog.ts @@ -0,0 +1,137 @@ +import { crLabelKey, DBReconfigStatusMap } from '@/constants/db'; +import { authSession } from '@/services/backend/auth'; +import { getK8s } from '@/services/backend/kubernetes'; +import { jsonRes } from '@/services/backend/response'; +import { ApiResp } from '@/services/kubernet'; +import { KubeBlockOpsRequestType } from '@/types/cluster'; +import { DBType, OpsRequestItemType } from '@/types/db'; +import { cpuFormatToC, memoryFormatToGi } from '@/utils/tools'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const { name } = req.query as { + name: string; + }; + + const { k8sCustomObjects, namespace } = await getK8s({ + kubeconfig: await authSession(req) + }); + + let labelSelector = `app.kubernetes.io/instance=${name},${crLabelKey}=${name}`; + + const opsrequestsList = (await k8sCustomObjects.listNamespacedCustomObject( + 'apps.kubeblocks.io', + 'v1alpha1', + namespace, + 'opsrequests', + undefined, + undefined, + undefined, + undefined, + labelSelector + )) as { + body: { + items: KubeBlockOpsRequestType[]; + }; + }; + + const opsrequests = opsrequestsList.body.items + .map((item) => { + const simpleTypes = ['Start', 'Stop', 'Restart']; + + const configurations = (() => { + if (simpleTypes.includes(item.spec.type)) { + return [{ parameterName: item.spec.type, newValue: '-', oldValue: '-' }]; + } + + if (item.spec.type === 'VerticalScaling') { + const componentName = item.spec?.verticalScaling?.[0]?.componentName!; + const oldConfig = item.status?.lastConfiguration?.components?.[componentName]; + const newConfig = item.spec?.verticalScaling?.[0]; + + const changedConfigs = []; + + if (oldConfig?.limits?.cpu !== newConfig?.limits?.cpu) { + changedConfigs.push({ + parameterName: item.spec.type + 'CPU', + newValue: cpuFormatToC(newConfig?.limits?.cpu) || '-', + oldValue: cpuFormatToC(oldConfig?.limits?.cpu) || '-' + }); + } + + if (oldConfig?.limits?.memory !== newConfig?.limits?.memory) { + changedConfigs.push({ + parameterName: item.spec.type + 'Memory', + newValue: memoryFormatToGi(newConfig?.limits?.memory) || '-', + oldValue: memoryFormatToGi(oldConfig?.limits?.memory) || '-' + }); + } + + return changedConfigs; + } + + if (item.spec.type === 'HorizontalScaling') { + const componentName = item.spec?.horizontalScaling?.[0]?.componentName!; + const oldReplicas = + item.status?.lastConfiguration?.components?.[componentName]?.replicas; + const newReplicas = item.spec?.horizontalScaling?.[0]?.replicas; + + return [ + { + parameterName: 'HorizontalScaling', + newValue: String(newReplicas || '-'), + oldValue: String(oldReplicas || '-') + } + ]; + } + + if (item.spec.type === 'VolumeExpansion') { + const componentName = item.spec?.volumeExpansion?.[0]?.componentName!; + const oldStorage = + item.status?.lastConfiguration?.components?.[componentName]?.volumeClaimTemplates?.[0] + ?.storage; + const newStorage = item.spec?.volumeExpansion?.[0]?.volumeClaimTemplates?.[0]?.storage; + + return [ + { + parameterName: 'VolumeExpansion', + newValue: String(newStorage || '-'), + oldValue: String(oldStorage || '-') + } + ]; + } + + return [ + { + parameterName: item.spec.type, + newValue: '-', + oldValue: '-' + } + ]; + })(); + + return { + id: item.metadata.uid, + name: item.metadata.name, + status: + item.status?.phase && DBReconfigStatusMap[item.status.phase] + ? DBReconfigStatusMap[item.status.phase] + : DBReconfigStatusMap.Creating, + startTime: new Date(item.metadata.creationTimestamp), + namespace: item.metadata.namespace, + configurations + }; + }) + .filter((item) => item.configurations.length > 0) as OpsRequestItemType[]; + + jsonRes(res, { + data: opsrequests + }); + } catch (err: any) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/frontend/providers/dbprovider/src/pages/api/pauseDBByName.ts b/frontend/providers/dbprovider/src/pages/api/pauseDBByName.ts index 602e14de0f3..feb4b5b9b55 100644 --- a/frontend/providers/dbprovider/src/pages/api/pauseDBByName.ts +++ b/frontend/providers/dbprovider/src/pages/api/pauseDBByName.ts @@ -3,9 +3,10 @@ import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; import { ApiResp } from '@/services/kubernet'; import { KbPgClusterType } from '@/types/cluster'; -import { json2StartOrStop } from '@/utils/json2Yaml'; +import { json2BasicOps } from '@/utils/json2Yaml'; import { PatchUtils } from '@kubernetes/client-node'; import type { NextApiRequest, NextApiResponse } from 'next'; +import { getCluster } from './getDBByName'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -24,20 +25,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< kubeconfig: await authSession(req) }); - const { yaml, yamlObj } = json2StartOrStop({ + const yaml = json2BasicOps({ dbName, type: 'Stop' }); - const { body } = (await k8sCustomObjects.getNamespacedCustomObject( - 'apps.kubeblocks.io', - 'v1alpha1', - namespace, - 'clusters', - dbName - )) as { - body: KbPgClusterType; - }; + const body = await getCluster(req, dbName); if (body.spec.backup?.enabled === true) { const patch = [ diff --git a/frontend/providers/dbprovider/src/pages/api/startDBByName.ts b/frontend/providers/dbprovider/src/pages/api/startDBByName.ts index ec8859284eb..0f545fc7349 100644 --- a/frontend/providers/dbprovider/src/pages/api/startDBByName.ts +++ b/frontend/providers/dbprovider/src/pages/api/startDBByName.ts @@ -3,9 +3,10 @@ import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; import { ApiResp } from '@/services/kubernet'; import { KbPgClusterType } from '@/types/cluster'; -import { json2StartOrStop } from '@/utils/json2Yaml'; +import { json2BasicOps } from '@/utils/json2Yaml'; import { PatchUtils } from '@kubernetes/client-node'; import type { NextApiRequest, NextApiResponse } from 'next'; +import { getCluster } from './getDBByName'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -24,22 +25,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< kubeconfig: await authSession(req) }); - const { yaml, yamlObj } = json2StartOrStop({ + const yaml = json2BasicOps({ dbName, type: 'Start' }); - const { body } = (await k8sCustomObjects.getNamespacedCustomObject( - 'apps.kubeblocks.io', - 'v1alpha1', - namespace, - 'clusters', - dbName - )) as { - body: KbPgClusterType; - }; - - console.log(yamlObj, body.spec.backup, body.spec.backup?.enabled === false, 'yaml'); + const body = await getCluster(req, dbName); if (body.spec.backup?.enabled === false) { const patch = [ diff --git a/frontend/providers/dbprovider/src/pages/backups/index.tsx b/frontend/providers/dbprovider/src/pages/backups/index.tsx new file mode 100644 index 00000000000..8fc735062f2 --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/backups/index.tsx @@ -0,0 +1,368 @@ +import { deleteBackup, getBackups } from '@/api/backup'; +import { getDBByName } from '@/api/db'; +import MyIcon from '@/components/Icon'; +import MyTooltip from '@/components/MyTooltip'; +import Sidebar from '@/components/Sidebar'; +import { BackupStatusEnum, backupTypeMap } from '@/constants/backup'; +import { useConfirm } from '@/hooks/useConfirm'; +import { useLoading } from '@/hooks/useLoading'; +import useEnvStore from '@/store/env'; +import { BackupItemType, DBDetailType } from '@/types/db'; +import { I18nCommonKey } from '@/types/i18next'; +import { serviceSideProps } from '@/utils/i18n'; +import { getErrText } from '@/utils/tools'; +import { QuestionOutlineIcon } from '@chakra-ui/icons'; +import { + Box, + Button, + Center, + Flex, + Image, + Input, + InputGroup, + InputLeftElement, + Table, + Tbody, + Td, + Text, + Th, + Thead, + Tr +} from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui'; +import { useQuery } from '@tanstack/react-query'; +import { + ColumnDef, + flexRender, + getCoreRowModel, + getFilteredRowModel, + useReactTable +} from '@tanstack/react-table'; +import dayjs from 'dayjs'; +import { groupBy } from 'lodash'; +import { useTranslation } from 'next-i18next'; +import { useRouter } from 'next/router'; +import React, { useCallback, useMemo, useState } from 'react'; +import RestoreModal from '../db/detail/components/RestoreModal'; + +const operationIconStyles = { + w: '18px' +}; + +export default function Backups() { + const { t } = useTranslation(); + const router = useRouter(); + const [globalFilter, setGlobalFilter] = useState(''); + const [backupInfo, setBackupInfo] = useState(); + const { SystemEnv } = useEnvStore(); + const { message: toast } = useMessage(); + const { Loading, setIsLoading } = useLoading(); + const [expandedGroups, setExpandedGroups] = useState>({}); + + const { openConfirm: openConfirmDel, ConfirmChild: ConfirmDelChild } = useConfirm({ + content: t('confirm_delete_the_backup') + }); + + const [db, setDb] = useState(); + + const loadDBDetail = useCallback( + async (dbName: string) => { + try { + const res = await getDBByName(dbName); + setDb(res); + } catch (err) { + toast({ + title: getErrText(err), + status: 'error' + }); + } + }, + [toast] + ); + + const { data, refetch, isLoading } = useQuery(['getBackupList'], getBackups, { + onSuccess: (data) => { + if (data.length > 0 && Object.keys(expandedGroups).length === 0) { + const firstDbName = data[0].dbName; + setExpandedGroups((prev) => ({ + ...prev, + [firstDbName]: true + })); + } + }, + refetchInterval: 60000 + }); + + const confirmDel = useCallback( + async (name: string) => { + try { + setIsLoading(true); + await deleteBackup(name); + await refetch(); + toast({ + title: t('Success'), + status: 'success' + }); + } catch (err) { + toast({ + title: getErrText(err), + status: 'error' + }); + } + setIsLoading(false); + }, + [refetch, setIsLoading, toast] + ); + + const columns = useMemo>>( + () => [ + { + id: 'name', + accessorKey: 'name', + header: () => t('name') + }, + { + id: 'remark', + accessorKey: 'remark', + header: () => t('remark') + }, + { + id: 'status', + accessorKey: 'status', + header: () => t('status'), + cell: ({ row }) => ( + + {t(row.original.status.label as I18nCommonKey)} + {row.original.failureReason && ( + + + + )} + + ) + }, + { + id: 'backupTime', + accessorKey: 'startTime', + header: () => t('backup_time'), + cell: ({ row }) => dayjs(row.original.startTime).format('YYYY/MM/DD HH:mm') + }, + { + id: 'type', + accessorKey: 'type', + header: () => t('Type'), + cell: ({ row }) => t(backupTypeMap[row.original.type]?.label) || '-' + }, + { + id: 'actions', + header: () => t('operation'), + cell: ({ row }) => + row.original.status.value !== BackupStatusEnum.InProgress ? ( + + + + + + + + + ) : null + } + ], + [confirmDel, openConfirmDel, t] + ); + + const table = useReactTable({ + data: data || [], + columns, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + state: { + globalFilter + }, + onGlobalFilterChange: setGlobalFilter, + globalFilterFn: (row, columnId, filterValue) => { + const name = row.original.name.toLowerCase().includes(filterValue.toLowerCase()); + const remark = row.original.remark.toLowerCase().includes(filterValue.toLowerCase()); + return name || remark; + } + }); + + return ( + + + + + + {t('backup_center')} + + + + + + + + table.setGlobalFilter(e.target.value)} + /> + + +
+ )} + {...tdStyle} + > {flexRender(cell.column.columnDef.cell, cell.getContext())}
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {Object.entries(groupBy(table.getRowModel().rows, (row) => row.original.dbName)).map( + ([dbName, rows]) => ( + + + + + {expandedGroups[dbName] && + rows.map((row, index) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + + ) + )} + +
+ {flexRender(header.column.columnDef.header, header.getContext())} +
+ +
+ setExpandedGroups((prev) => ({ + ...prev, + [dbName]: !prev[dbName] + })) + } + > + +
+ + {dbName} + + {dbName} +
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+ {data?.length === 0 && ( + + + {t('no_data_available')} + + )} +
+ + {!!backupInfo?.name && db && ( + setBackupInfo(undefined)} /> + )} +
+ ); +} + +export async function getServerSideProps(content: any) { + return { + props: { + ...(await serviceSideProps(content)) + } + }; +} diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx index e15951ec28a..d62d61936b8 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx @@ -19,11 +19,15 @@ import { Center, Divider, Flex, + FlexProps, Modal, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, + Skeleton, + SkeletonText, + Stack, Switch, Text, useDisclosure @@ -35,6 +39,55 @@ import { useTranslation } from 'next-i18next'; import { useCallback, useMemo, useState } from 'react'; import { sealosApp } from 'sealos-desktop-sdk/app'; +const CopyBox = ({ + value, + showSecret = true, + boxStyle +}: { + value: string; + showSecret?: boolean; + boxStyle?: FlexProps; +}) => { + const { copyData } = useCopyData(); + + const defaultBoxStyle: FlexProps = { + borderRadius: '4px', + bg: 'grayModern.25', + h: '32px', + p: '8px 32px 8px 12px', + border: '1px solid', + borderColor: 'grayModern.100', + color: 'grayModern.900', + noOfLines: 1 + }; + + return ( + + + {showSecret ? value : '***********'} + +
copyData(value)} + cursor="pointer" + > + +
+
+ ); +}; + const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { const { t } = useTranslation(); const { copyData } = useCopyData(); @@ -221,110 +274,106 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { }; return ( - - {db?.source?.hasSource && ( - - - - {t('application_source')} - - + + + {appInfoTable.map((info, index) => ( + { - if (!db.source.sourceName) return; - if (db.source.sourceType === 'app_store') { - sealosApp.runEvents('openDesktopApp', { - appKey: 'system-template', - pathname: '/instance', - query: { instanceName: db.source.sourceName } - }); - } - if (db.source.sourceType === 'sealaf') { - sealosApp.runEvents('openDesktopApp', { - appKey: 'system-sealaf', - pathname: '/', - query: { instanceName: db.source.sourceName } - }); - } - }} + alignItems={'center'} + gap={'8px'} + color={'grayModern.900'} + fontWeight={'bold'} + fontSize={'16px'} > - - {t(db.source.sourceType)} - - {t('manage_all_resources')} - + {t(info.name)} - - - )} - {appInfoTable.map((info) => ( - - - - {t(info.name)} - - - - {info.items.map((item, i) => ( - - - {t(item.label)} + + {db?.source?.hasSource && index === 0 && ( + + { + if (!db.source.sourceName) return; + if (db.source.sourceType === 'app_store') { + sealosApp.runEvents('openDesktopApp', { + appKey: 'system-template', + pathname: '/instance', + query: { instanceName: db.source.sourceName } + }); + } + if (db.source.sourceType === 'sealaf') { + sealosApp.runEvents('openDesktopApp', { + appKey: 'system-sealaf', + pathname: '/', + query: { instanceName: db.source.sourceName } + }); + } + }} + > + + {t('application_source')} + + + {t(db.source.sourceType)} + + {t('manage_all_resources')} + + + - ( + - - item.value && !!item.copy && copyData(item.copy)} - > - {item.value} - - - - - ))} + + {t(item.label)} + + + + item.value && !!item.copy && copyData(item.copy)} + > + {item.value} + + + + + ))} + + {index !== appInfoTable.length - 1 && } - - ))} - {/* secret */} - {secret && ( - <> - - - {t('connection_info')} + ))} + + {secret ? ( + + + + {t('connection_info')} +
setShowSecret(!showSecret)} @@ -341,7 +390,8 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { gap={'6px'} h="28px" fontSize={'12px'} - bg="grayModern.150" + bg="white" + border="1px solid #DFE2EA" borderRadius={'md'} px="8px" cursor={'pointer'} @@ -355,97 +405,78 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { {t('direct_connection')}
)} - -
- {t('external_network')} - (isChecked ? closeNetWorkService() : onOpen())} - /> -
{['milvus', 'kafka'].indexOf(db.dbType) === -1 && ( - + {Object.entries(baseSecret).map(([name, value]) => ( - - {name} - - copyData(value)}> - {showSecret ? value : '***********'} - + + + {name} + ))} - + )} - - {t('intranet_address')} - - {Object.entries(otherSecret).map(([name, value]) => ( - - {name} - - copyData(value)}> - {showSecret ? value : '***********'} + + + + {t('intranet_address')} + + + {Object.entries(otherSecret).map(([name, value], index) => ( + + + {name} + - - ))} + ))} +
- {isChecked && ( - - {t('external_address')} - - {Object.entries(externalNetWork).map(([name, value]) => ( - - {name} - - copyData(value)}> - {showSecret ? value : '***********'} + + + + + {t('external_address')} + + (isChecked ? closeNetWorkService() : onOpen())} + /> + + {isChecked ? ( + + {Object.entries(externalNetWork).map(([name, value], index) => ( + + + {name} + - - ))} - - )} + ))} +
+ ) : ( +
+ {t('no_data_available')} +
+ )} + @@ -487,9 +518,29 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => {
- + + ) : ( + + + + + )} - + ); }; diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/BackupTable.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/BackupTable.tsx index caa7aa51fad..5238bce078b 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/BackupTable.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/BackupTable.tsx @@ -15,6 +15,7 @@ import { TableContainer, Tbody, Td, + Text, Th, Thead, Tooltip, @@ -66,7 +67,7 @@ const BackupTable = ({ db }: { db?: DBDetailType }, ref: ForwardedRef { const backups: BackupItemType[] = await getBackupList(db.dbName); backups.sort((a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime()); @@ -110,6 +111,7 @@ const BackupTable = ({ db }: { db?: DBDetailType }, ref: ForwardedRef + + + {t('backup_list')} + + {!backupProcessing && ( + + )} + - + {columns.map((item) => ( diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/DataImport.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/DataImport.tsx new file mode 100644 index 00000000000..51d18f52586 --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/DataImport.tsx @@ -0,0 +1,75 @@ +import { DBDetailType } from '@/types/db'; +import { Box, Button, Flex } from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; +import { useState } from 'react'; +import { MigrateTable } from './Migrate/Table'; +import DumpImport from './DumpImport'; +import useEnvStore from '@/store/env'; +import { useRouter } from 'next/router'; + +enum MenuType { + DumpImport = 'DumpImport', + InternetMigration = 'InternetMigration' +} + +export default function DataImport({ db }: { db?: DBDetailType }) { + if (!db) return null; + + const { t } = useTranslation(); + const [activeId, setActiveId] = useState(MenuType.InternetMigration); + const { SystemEnv } = useEnvStore(); + const router = useRouter(); + + return ( + + + + {[ + { id: MenuType.InternetMigration, label: t('online_import') }, + ...(!!SystemEnv.minio_url + ? [{ id: MenuType.DumpImport, label: t('import_through_file') }] + : []) + ].map((item) => ( + { + setActiveId(item.id); + } + })} + > + {item.label} + + ))} + + {activeId === MenuType.InternetMigration && ( + + )} + + {activeId === MenuType.InternetMigration && } + {activeId === MenuType.DumpImport && } + + ); +} diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/DumpImport/index.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/DumpImport/index.tsx index f430ab1bc31..a780230aee5 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/DumpImport/index.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/DumpImport/index.tsx @@ -196,8 +196,8 @@ export default function DumpImport({ db }: { db?: DBDetailType }) { return ( - - + + {t('upload_dump_file')} diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/ErrorLog/RunTimeLog.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/ErrorLog/RunTimeLog.tsx index ae191a09c69..ec40294acd1 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/ErrorLog/RunTimeLog.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/ErrorLog/RunTimeLog.tsx @@ -190,18 +190,21 @@ export default function RunTimeLog({ return ( - + {filteredSubNavList?.map((item) => ( item.value !== logType && updateSubMenu(item.value)} + fontWeight={'500'} > {t(item.label as I18nCommonKey)} @@ -269,7 +272,7 @@ export default function RunTimeLog({ /> )} - + @@ -280,7 +283,12 @@ export default function RunTimeLog({ /> - + + router.replace('/dbs')}> - - + + {router.query.name || db.dbName} - {!isLargeScreen && ( + {/* {!isLargeScreen && ( - )} + )} */} + {/* btns */} {/* Migrate */} {/* */} {db.status.value !== 'Stopped' && ( ) : (
{t(item.title)}
+ + + {historyColumns.map((item) => ( + + ))} + + + + {operationList?.map((app, appIndex) => { + return ( + + {app.configurations?.map((item, configIndex) => { + return ( + + {historyColumns.map((col) => ( + + ))} + + ); + })} + {appIndex < operationList.length - 1 && ( + + + + )} + + ); + })} + +
+ {t(item.title)} +
+ {col.render + ? col.render(app, configIndex) + : col.dataIndex + ? `${app[col.dataIndex]}` + : '-'} +
+ +
+
+ + ); +} diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/Pods.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/Pods.tsx index 64651d2e85a..3736fdca9b0 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/Pods.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/Pods.tsx @@ -117,10 +117,10 @@ const Pods = ({ dbName, dbType }: { dbName: string; dbType: string }) => { }); return ( - - + + - + {columns.map((item) => ( diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/Reconfigure/ConfigTable.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/Reconfigure/ConfigTable.tsx index f23b7e58206..c93ade7d165 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/Reconfigure/ConfigTable.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/Reconfigure/ConfigTable.tsx @@ -154,7 +154,7 @@ const ConfigTable = forwardRef< return ( - + - + {SubNavList.map((item) => ( - + {subMenu === SubMenuEnum.Parameter && config && ( { const PublicNetMigration = ['postgresql', 'apecloud-mysql', 'mongodb'].includes(dbType); const MigrateSupported = ['postgresql', 'mongodb', 'apecloud-mysql'].includes(dbType); const BackupSupported = BackupSupportedDBTypeList.includes(dbType) && SystemEnv.BACKUP_ENABLED; const listNavValue = [ + { + label: t('overview'), + value: TabEnum.Overview, + icon: + }, { label: 'monitor_list', value: TabEnum.monitor, icon: }, { - label: 'replicas_list', - value: TabEnum.pod, - icon: + label: 'change_log', + value: TabEnum.OperationLog, + icon: }, ...(PublicNetMigration ? [ @@ -87,21 +97,12 @@ const AppDetail = ({ ...(PublicNetMigration ? [ { - label: 'online_import', - value: TabEnum.InternetMigration, + label: 'data_import', + value: TabEnum.DataImport, icon: } ] : []), - ...(PublicNetMigration && !!SystemEnv.minio_url - ? [ - { - label: 'import_through_file', - value: TabEnum.DumpImport, - icon: - } - ] - : []), ...(BackupSupported ? [ { @@ -119,7 +120,7 @@ const AppDetail = ({ isBackupSupported: BackupSupported, listNav: listNavValue }; - }, [SystemEnv, dbType]); + }, [SystemEnv.BACKUP_ENABLED, dbType, t]); const theme = useTheme(); const { message: toast } = useMessage(); @@ -141,118 +142,108 @@ const AppDetail = ({ }); return ( - +
- - - {dbDetail ? : } - + - - {listNav.map((item) => ( - - router.replace( - `/db/detail?name=${dbName}&dbType=${dbType}&listType=${item.value}` - ) - })} - > - {item.icon} - - {t(item.label as I18nCommonKey)} - - ))} - - {listType === TabEnum.pod && {dbPods.length} Items} - {listType === TabEnum.backup && !BackupTableRef.current?.backupProcessing && ( - - - - )} - {listType === TabEnum.InternetMigration && ( - - - - )} + {item.icon} + + ) : ( + {item.icon} + )} + + {!isSmallScreen && {t(item.label as I18nCommonKey)}} + + ))} + + {listType === TabEnum.Overview ? ( + + + + + {t('replicas_list')} + + + - - {listType === TabEnum.pod && } + ) : ( + {listType === TabEnum.backup && } + {listType === TabEnum.monitor && ( )} - {listType === TabEnum.InternetMigration && } - {listType === TabEnum.DumpImport && } + + {listType === TabEnum.DataImport && } + {listType === TabEnum.Reconfigure && ( )} + {listType === TabEnum.ErrorLog && } + + {listType === TabEnum.OperationLog && } - + )} + {/* mask */} {!isLargeScreen && showSlider && ( import('@/pages/db/detail/components/DelModal')); @@ -116,209 +120,251 @@ const DBList = ({ [refetchApps, setLoading, t, toast] ); - const columns: { - title: string; - dataIndex?: keyof DBListItemType; - key: string; - render?: (item: DBListItemType) => JSX.Element; - }[] = [ - { - title: t('name'), - key: 'name', - render: (item: DBListItemType) => { - return ( - - {item.name} + const columns = useMemo>>( + () => [ + { + id: 'name', + accessorKey: 'name', + header: () => t('name'), + cell: ({ row }) => ( + + {row.original.name} - ); - } - }, - { - title: t('Type'), - key: 'dbType', - render: (item: DBListItemType) => ( - - - {DBComponentNameMap[item.dbType]} - - ) - }, - { - title: t('status'), - key: 'status', - render: (item: DBListItemType) => ( - - ) - }, - { - title: t('creation_time'), - dataIndex: 'createTime', - key: 'createTime' - }, - { - title: t('cpu'), - key: 'cpu', - render: (item: DBListItemType) => <>{item.cpu / 1000}C - }, - { - title: t('memory'), - key: 'memory', - render: (item: DBListItemType) => <>{printMemory(item.memory)} - }, - { - title: t('storage'), - key: 'storage', - dataIndex: 'storage' - }, - { - title: t('operation'), - key: 'control', - render: (item: DBListItemType) => ( - - - - - - } - menuList={[ - ...(item.status.value === DBStatusEnum.Stopped - ? [ - { - child: ( - <> - - {t('Continue')} - - ), - onClick: () => handleStartApp(item) - } - ] - : [ - { - child: ( - <> - - {t('update')} - - ), - onClick: () => { - if (item.source.hasSource && item.source.sourceType === 'sealaf') { - setUpdateAppName(item.name); - onOpenUpdateModal(); - } else { - router.push(`/db/edit?name=${item.name}`); - } + ) + }, + { + accessorKey: 'dbType', + header: () => t('Type'), + cell: ({ row }) => ( + + + {DBTypeList.find((i) => i.id === row.original.dbType)?.label} + + ) + }, + { + accessorKey: 'status', + header: () => t('status'), + cell: ({ row }) => ( + + ) + }, + { + accessorKey: 'createTime', + header: () => t('creation_time') + }, + { + accessorKey: 'cpu', + header: () => t('cpu'), + cell: ({ row }) => <>{row.original.cpu / 1000}C + }, + { + accessorKey: 'memory', + header: () => t('memory'), + cell: ({ row }) => <>{printMemory(row.original.memory)} + }, + { + accessorKey: 'storage', + header: () => t('storage') + }, + { + id: 'actions', + header: () => t('operation'), + cell: ({ row }) => ( + + + + + + + } + menuList={[ + ...(row.original.status.value === DBStatusEnum.Stopped + ? [ + { + child: ( + <> + + {t('Continue')} + + ), + onClick: () => handleStartApp(row.original) + } + ] + : [ + { + child: ( + <> + + {t('update')} + + ), + onClick: () => { + if ( + row.original.source.hasSource && + row.original.source.sourceType === 'sealaf' + ) { + setUpdateAppName(row.original.name); + onOpenUpdateModal(); + } else { + router.push(`/db/edit?name=${row.original.name}`); + } + }, + isDisabled: + row.original.status.value === 'Updating' && + !row.original.isDiskSpaceOverflow }, - isDisabled: item.status.value === 'Updating' && !item.isDiskSpaceOverflow - }, - { - child: ( - <> - - {t('Restart')} - - ), - onClick: () => handleRestartApp(item), - isDisabled: item.status.value === 'Updating' - } - ]), - ...(item.status.value === DBStatusEnum.Running - ? [ - { - child: ( - <> - - {t('Pause')} - - ), - onClick: onOpenPause(() => handlePauseApp(item)) + { + child: ( + <> + + {t('Restart')} + + ), + onClick: () => handleRestartApp(row.original), + isDisabled: row.original.status.value === 'Updating' + } + ]), + ...(row.original.status.value === DBStatusEnum.Running + ? [ + { + child: ( + <> + + {t('Pause')} + + ), + onClick: onOpenPause(() => handlePauseApp(row.original)) + } + ] + : []), + + { + child: ( + <> + + {t('Delete')} + + ), + menuItemStyle: { + _hover: { + color: 'red.600', + bg: 'rgba(17, 24, 36, 0.05)' } - ] - : []), + }, + onClick: () => setDelAppName(row.original.name), + isDisabled: row.original.status.value === 'Updating' + } + ]} + /> + + ) + } + ], + [] + ); - { - child: ( - <> - - {t('Delete')} - - ), - menuItemStyle: { - _hover: { - color: 'red.600', - bg: 'rgba(17, 24, 36, 0.05)' - } - }, - onClick: () => setDelAppName(item.name), - isDisabled: item.status.value === 'Updating' - } - ]} - /> - - ) - } - ]; + const table = useReactTable({ + data: dbList, + columns, + initialState: { + columnPinning: { + left: ['name'], + right: ['actions'] + } + }, + // enableColumnPinning: true, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel() + }); return ( - - -
- -
+ + {t('DBList')} - - ( {dbList.length} ) - +
+ {dbList.length} +
- {SystemEnv?.SHOW_DOCUMENT && ( - - )}
- + + + {!!delAppName && ( { flexDirection="column" alignItems="center" justifyContent="center" - bg={'#F3F4F5'} + backgroundColor={'white'} + px={'32px'} + h={'full'} + w={'full'} + borderRadius={'xl'} > {t('database_empty')} diff --git a/frontend/providers/dbprovider/src/pages/dbs/index.tsx b/frontend/providers/dbprovider/src/pages/dbs/index.tsx index cc3be741895..0c1bb91669c 100644 --- a/frontend/providers/dbprovider/src/pages/dbs/index.tsx +++ b/frontend/providers/dbprovider/src/pages/dbs/index.tsx @@ -5,6 +5,8 @@ import { useDBStore } from '@/store/db'; import { useLoading } from '@/hooks/useLoading'; import { useState } from 'react'; import { serviceSideProps } from '@/utils/i18n'; +import Sidebar from '@/components/Sidebar'; +import { Flex } from '@chakra-ui/react'; function Home() { const { dbList, setDBList } = useDBStore(); @@ -20,13 +22,14 @@ function Home() { return ( <> - {dbList.length === 0 && initialized ? ( - - ) : ( - <> + + + {dbList.length === 0 && initialized ? ( + + ) : ( - - )} + )} + ); diff --git a/frontend/providers/dbprovider/src/pages/icons/index.tsx b/frontend/providers/dbprovider/src/pages/icons/index.tsx new file mode 100644 index 00000000000..07068676609 --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/icons/index.tsx @@ -0,0 +1,51 @@ +import MyIcon, { IconMap } from '@/components/Icon'; +import { useCopyData } from '@/utils/tools'; +import { Box, SimpleGrid } from '@chakra-ui/react'; +import { useRouter } from 'next/router'; +import { useEffect } from 'react'; + +export default function IconsPage() { + const router = useRouter(); + const iconNames = Object.keys(IconMap) as Array; + const { copyData } = useCopyData(); + + const copyIconName = (iconName: string) => { + const iconCode = ``; + copyData(iconCode); + }; + + useEffect(() => { + if (process.env.NODE_ENV === 'production') { + router.replace('/dbs'); + } + }, [router]); + + return ( + + + {iconNames.map((iconName) => ( + copyIconName(iconName)} + display="flex" + flexDirection="column" + alignItems="center" + _hover={{ bg: 'grayModern.50' }} + > + + + + + {iconName} + + + ))} + + + ); +} diff --git a/frontend/providers/dbprovider/src/pages/redirect.tsx b/frontend/providers/dbprovider/src/pages/redirect.tsx new file mode 100644 index 00000000000..fd375a35d9a --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/redirect.tsx @@ -0,0 +1,37 @@ +import { getDBByName } from '@/api/db'; +import { useGlobalStore } from '@/store/global'; +import { useRouter } from 'next/router'; +import { useEffect } from 'react'; + +const RedirectPage = () => { + const router = useRouter(); + const { setLastRoute } = useGlobalStore(); + + useEffect(() => { + const handleRedirect = (name?: string) => { + if (name) { + getDBByName(name) + .then((app) => { + router.replace({ + pathname: '/db/detail', + query: { name: app.dbName, dbType: app.dbType } + }); + }) + .catch((err) => { + router.replace('/dbs'); + }); + } else { + router.replace('/dbs'); + } + }; + + if (router.isReady) { + const { name } = router.query as { name?: string }; + handleRedirect(name); + } + }, [router, router.isReady, router.query]); + + return null; +}; + +export default RedirectPage; diff --git a/frontend/providers/dbprovider/src/types/cluster.d.ts b/frontend/providers/dbprovider/src/types/cluster.d.ts index 3ca22bead0d..b6115f94cc1 100644 --- a/frontend/providers/dbprovider/src/types/cluster.d.ts +++ b/frontend/providers/dbprovider/src/types/cluster.d.ts @@ -149,8 +149,47 @@ export type KubeBlockOpsRequestType = { }; ttlSecondsBeforeAbort: number; type: string; + verticalScaling?: { + componentName: string; + limits: { + cpu: string; + memory: string; + }; + requests: { + cpu: string; + memory: string; + }; + }[]; + horizontalScaling?: { + componentName: string; + replicas: number; + }[]; + volumeExpansion?: { + componentName: string; + volumeClaimTemplates: { + storage: string; + }[]; + }[]; }; status: { + lastConfiguration: { + components: { + [key: string]: { + limits: { + cpu: string; + memory: string; + }; + requests: { + cpu: string; + memory: string; + }; + replicas: number; + volumeClaimTemplates: { + storage: string; + }[]; + }; + }; + }; clusterGeneration: number; completionTimestamp: string; conditions: { diff --git a/frontend/providers/dbprovider/src/types/db.d.ts b/frontend/providers/dbprovider/src/types/db.d.ts index 48fe96769ff..07a5a24e8b6 100644 --- a/frontend/providers/dbprovider/src/types/db.d.ts +++ b/frontend/providers/dbprovider/src/types/db.d.ts @@ -138,6 +138,8 @@ export interface BackupItemType { type: `${BackupTypeEnum}`; namespace: string; connectionPassword: string; + dbName: string; + dbType: string; } export type ReconfigStatusMapType = { diff --git a/frontend/providers/dbprovider/src/utils/adapt.ts b/frontend/providers/dbprovider/src/utils/adapt.ts index d43342f0d3a..5a5e5397f62 100644 --- a/frontend/providers/dbprovider/src/utils/adapt.ts +++ b/frontend/providers/dbprovider/src/utils/adapt.ts @@ -1,6 +1,7 @@ import { BACKUP_REMARK_LABEL_KEY, BackupTypeEnum, backupStatusMap } from '@/constants/backup'; import { DBBackupMethodNameMap, + DBNameLabel, DBPreviousConfigKey, DBReconfigStatusMap, DBSourceConfigs, @@ -218,6 +219,7 @@ export const adaptBackup = (backup: BackupCRItemType): BackupItemType => { const autoLabel = 'dataprotection.kubeblocks.io/autobackup'; const passwordLabel = 'dataprotection.kubeblocks.io/connection-password'; const remark = backup.metadata.labels[BACKUP_REMARK_LABEL_KEY]; + const dbType = backup.metadata.labels['apps.kubeblocks.io/component-name'] || 'postgresql'; return { id: backup.metadata.uid, @@ -231,7 +233,9 @@ export const adaptBackup = (backup: BackupCRItemType): BackupItemType => { type: autoLabel in backup.metadata.labels ? BackupTypeEnum.auto : BackupTypeEnum.manual, remark: remark ? decodeFromHex(remark) : '-', failureReason: backup.status?.failureReason, - connectionPassword: backup.metadata?.annotations?.[passwordLabel] + connectionPassword: backup.metadata?.annotations?.[passwordLabel], + dbName: backup.metadata.labels[DBNameLabel], + dbType: dbType === 'mysql' ? 'apecloud-mysql' : dbType }; }; diff --git a/frontend/providers/dbprovider/src/utils/json2Yaml.ts b/frontend/providers/dbprovider/src/utils/json2Yaml.ts index 6c581cd1491..ef2e5f561ac 100644 --- a/frontend/providers/dbprovider/src/utils/json2Yaml.ts +++ b/frontend/providers/dbprovider/src/utils/json2Yaml.ts @@ -29,7 +29,13 @@ const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5); * @param backupInfo Optional backup data for database restoration. * @returns Generated YAML configuration. */ -export const json2CreateCluster = (data: DBEditType, backupInfo?: BackupItemType) => { +export const json2CreateCluster = ( + data: DBEditType, + backupInfo?: BackupItemType, + options?: { + storageClassName?: string; + } +) => { const resources = { limits: { cpu: `${str2Num(Math.floor(data.cpu))}m`, @@ -65,7 +71,10 @@ export const json2CreateCluster = (data: DBEditType, backupInfo?: BackupItemType name: data.dbName }; - const storageClassName = StorageClassName ? { storageClassName: StorageClassName } : {}; + const storageClassName = + options?.storageClassName || StorageClassName + ? { storageClassName: options?.storageClassName || StorageClassName } + : {}; const redisHA = RedisHAConfig(data.replicas > 1); @@ -854,6 +863,9 @@ export const json2Upgrade = ({ dbName, dbVersion }: DBEditType) => { return yaml.dump(template); }; +/** + * @deprecated + */ export const json2StartOrStop = ({ dbName, type }: { dbName: string; type: 'Start' | 'Stop' }) => { const nameType = type.toLocaleLowerCase(); @@ -877,6 +889,9 @@ export const json2StartOrStop = ({ dbName, type }: { dbName: string; type: 'Star }; }; +/** + * @deprecated + */ export const json2Restart = ({ dbName, dbType }: { dbName: string; dbType: DBType }) => { const template = { apiVersion: 'apps.kubeblocks.io/v1alpha1', @@ -1176,7 +1191,7 @@ export const json2Reconfigure = ( return yaml.dump(template); }; -export const json2ClusterOps = ( +export const json2ResourceOps = ( data: DBEditType, type: 'VerticalScaling' | 'HorizontalScaling' | 'VolumeExpansion' ) => { @@ -1184,8 +1199,9 @@ export const json2ClusterOps = ( data.dbType === 'apecloud-mysql' ? 'mysql' : data.dbType === 'kafka' ? 'broker' : data.dbType; const getOpsName = () => { - const timeStr = dayjs().format('YYYYMMDDHHmmss'); - return `ops-${type.toLowerCase()}-${timeStr}`; + const timeStr = dayjs().format('YYYYMMDDHHmm'); + const randomStr = nanoid(4); + return `ops-${type.toLowerCase()}-${timeStr}-${randomStr}`; }; const baseTemplate = { @@ -1252,3 +1268,34 @@ export const json2ClusterOps = ( return yaml.dump(template); }; + +export const json2BasicOps = (data: { + dbName: string; + dbType?: DBType; + type: 'Start' | 'Stop' | 'Restart'; +}) => { + const componentName = + data.dbType === 'apecloud-mysql' ? 'mysql' : data.dbType === 'kafka' ? 'broker' : data.dbType; + + const template = { + apiVersion: 'apps.kubeblocks.io/v1alpha1', + kind: 'OpsRequest', + metadata: { + name: `ops-${data.type.toLowerCase()}-${dayjs().format('YYYYMMDDHHmmss')}`, + labels: { + [crLabelKey]: data.dbName + } + }, + spec: { + clusterRef: data.dbName, + type: data.type, + ...(data.type === 'Restart' + ? { + restart: [{ componentName }] + } + : {}) + } + }; + + return yaml.dump(template); +}; diff --git a/frontend/providers/dbprovider/src/utils/tools.ts b/frontend/providers/dbprovider/src/utils/tools.ts index 1fdf7148605..2447d12866c 100644 --- a/frontend/providers/dbprovider/src/utils/tools.ts +++ b/frontend/providers/dbprovider/src/utils/tools.ts @@ -95,6 +95,30 @@ export const strToBase64 = (str: string) => { return ''; }; +/** + * Format CPU value to standard C format + * @param cpu CPU value, like "500m", "1", "2" + * @returns Standardized CPU value with C suffix, like "0.5C", "1C", "2C" + */ +export const cpuFormatToC = (cpu: string | number = '0'): string => { + if (!cpu || cpu === '0') { + return '0C'; + } + + let value: number; + const cpuStr = cpu.toString(); + + if (/m$/i.test(cpuStr)) { + // Handle values with 'm' suffix, like "500m" + value = parseFloat(cpuStr) / 1000; + } else { + // Handle values without unit, like "1", "2" + value = parseFloat(cpuStr); + } + + return `${value.toFixed(1)}C`; +}; + /** * cpu format */ @@ -143,6 +167,39 @@ export const memoryFormatToMi = (memory = '0') => { return Number(value.toFixed(2)); }; +/** + * Format memory value to standard Gi format + * @param memory Memory value, like "512Mi", "1Gi", "2048Mi" + * @returns Standardized memory value with Gi suffix, like "0.5Gi", "1Gi", "2Gi" + */ +export const memoryFormatToGi = (memory: string | number = '0'): string => { + if (!memory || memory === '0') { + return '0Gi'; + } + + let value: number; + const memoryStr = memory.toString(); + + if (/Mi$/i.test(memoryStr)) { + // Convert Mi to Gi + value = parseFloat(memoryStr) / 1024; + } else if (/Gi$/i.test(memoryStr)) { + // Already in Gi + value = parseFloat(memoryStr); + } else if (/Ti$/i.test(memoryStr)) { + // Convert Ti to Gi + value = parseFloat(memoryStr) * 1024; + } else if (/Ki$/i.test(memoryStr)) { + // Convert Ki to Gi + value = parseFloat(memoryStr) / 1024 / 1024; + } else { + // Assume the value is in bytes and convert to Gi + value = parseFloat(memoryStr) / 1024 / 1024 / 1024; + } + + return `${value.toFixed(1)}Gi`; +}; + /** * storage format */ diff --git a/frontend/providers/devbox/.env.template b/frontend/providers/devbox/.env.template index c0bc5d8b6a4..156ba6ef066 100644 --- a/frontend/providers/devbox/.env.template +++ b/frontend/providers/devbox/.env.template @@ -6,3 +6,6 @@ DEVBOX_AFFINITY_ENABLE= SQUASH_ENABLE= NODE_TLS_REJECT_UNAUTHORIZED= ROOT_RUNTIME_NAMESPACE= +DATABASE_URL= +RETAG_SVC_URL= +PRIVACY_URL= diff --git a/frontend/providers/devbox/.eslintrc.json b/frontend/providers/devbox/.eslintrc.json index bffb357a712..cce8c3061f3 100644 --- a/frontend/providers/devbox/.eslintrc.json +++ b/frontend/providers/devbox/.eslintrc.json @@ -1,3 +1,23 @@ { - "extends": "next/core-web-vitals" -} + "extends": "next/core-web-vitals", + "rules": { + // Consistently import navigation APIs from `@/i18n` + "no-restricted-imports": [ + "error", + { + "name": "next/link", + "message": "Please import from `@/i18n` instead." + }, + { + "name": "next/navigation", + "importNames": [ + "redirect", + "permanentRedirect", + "useRouter", + "usePathname" + ], + "message": "Please import from `@/i18n` instead." + } + ] + } +} \ No newline at end of file diff --git a/frontend/providers/devbox/.gitignore b/frontend/providers/devbox/.gitignore index fd3dbb571a1..ab95baa5994 100644 --- a/frontend/providers/devbox/.gitignore +++ b/frontend/providers/devbox/.gitignore @@ -34,3 +34,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +prisma/generated \ No newline at end of file diff --git a/frontend/providers/devbox/api/devbox.ts b/frontend/providers/devbox/api/devbox.ts index 6cec0b4fc2e..9f94747da1e 100644 --- a/frontend/providers/devbox/api/devbox.ts +++ b/frontend/providers/devbox/api/devbox.ts @@ -1,40 +1,42 @@ import { V1Deployment, V1Pod, V1StatefulSet } from '@kubernetes/client-node' +import { DELETE, GET, POST } from '@/services/request' +import { GetDevboxByNameReturn } from '@/types/adapt' import { - DevboxEditType, - DevboxListItemType, + DevboxEditTypeV2, + DevboxListItemTypeV2, DevboxPatchPropsType, - DevboxVersionListItemType, - runtimeNamespaceMapType + DevboxVersionListItemType } from '@/types/devbox' +import { KBDevboxReleaseType, KBDevboxTypeV2 } from '@/types/k8s' +import { MonitorDataResult, MonitorQueryKey } from '@/types/monitor' import { adaptAppListItem, - adaptDevboxDetail, - adaptDevboxListItem, + adaptDevboxDetailV2, + adaptDevboxListItemV2, adaptDevboxVersionListItem, adaptPod } from '@/utils/adapt' -import { GET, POST, DELETE } from '@/services/request' -import { KBDevboxType, KBDevboxReleaseType } from '@/types/k8s' -import { MonitorDataResult, MonitorQueryKey } from '@/types/monitor' export const getMyDevboxList = () => - GET('/api/getDevboxList').then((data): DevboxListItemType[] => - data.map(adaptDevboxListItem).sort((a, b) => { + GET<[KBDevboxTypeV2, { + templateRepository: { + iconId: string | null; + }; + uid: string; + }][]>('/api/getDevboxList').then((data): DevboxListItemTypeV2[] => + data.map(adaptDevboxListItemV2).sort((a, b) => { return new Date(b.createTime).getTime() - new Date(a.createTime).getTime() }) ) export const getDevboxByName = (devboxName: string) => - GET('/api/getDevboxByName', { devboxName }).then((data) => - adaptDevboxDetail(data) - ) + GET('/api/getDevboxByName', { devboxName }).then(adaptDevboxDetailV2) export const applyYamlList = (yamlList: string[], type: 'create' | 'replace' | 'update') => POST('/api/applyYamlList', { yamlList, type }) export const createDevbox = (payload: { - devboxForm: DevboxEditType - runtimeNamespaceMap: runtimeNamespaceMapType + devboxForm: DevboxEditTypeV2 }) => POST(`/api/createDevbox`, payload) export const updateDevbox = (payload: { patch: DevboxPatchPropsType; devboxName: string }) => @@ -69,8 +71,16 @@ export const editDevboxVersion = (data: { name: string; releaseDes: string }) => export const delDevboxVersionByName = (versionName: string) => DELETE('/api/delDevboxVersionByName', { versionName }) -export const getSSHConnectionInfo = (data: { devboxName: string; runtimeName: string }) => - GET('/api/getSSHConnectionInfo', data) +export const getSSHConnectionInfo = (data: { devboxName: string }) => + GET<{ + base64PublicKey: string; + base64PrivateKey: string; + token: string; + userName: string; + workingDir: string; + releaseCommand: string; + releaseArgs: string; +}>('/api/getSSHConnectionInfo', data) export const getDevboxPodsByDevboxName = (name: string) => GET('/api/getDevboxPodsByDevboxName', { name }).then((item) => item.map(adaptPod)) @@ -81,9 +91,6 @@ export const getDevboxMonitorData = (payload: { step: string }) => GET(`/api/monitor/getMonitorData`, payload) -export const getSSHRuntimeInfo = (runtimeName: string) => - GET('/api/getSSHRuntimeInfo', { runtimeName }) - export const getAppsByDevboxId = (devboxId: string) => GET('/api/getAppsByDevboxId', { devboxId }).then((res) => res.map(adaptAppListItem) diff --git a/frontend/providers/devbox/api/platform.ts b/frontend/providers/devbox/api/platform.ts index 684763129e7..57f020171e3 100644 --- a/frontend/providers/devbox/api/platform.ts +++ b/frontend/providers/devbox/api/platform.ts @@ -1,6 +1,7 @@ import { GET, POST } from '@/services/request' -import type { UserQuotaItemType } from '@/types/user' +import type { UserQuotaItemType, UserTask } from '@/types/user' import type { Env } from '@/types/static' +import { getDesktopSessionFromSessionStorage, getSessionFromSessionStorage } from '@/utils/user' export const getAppEnv = () => GET('/api/getEnv') export const getUserQuota = () => @@ -14,3 +15,13 @@ export const getResourcePrice = () => GET('/api/platform/resourcePrice') export const postAuthCname = (data: { publicDomain: string; customDomain: string }) => POST('/api/platform/authCname', data) + +export const getUserTasks = () => + POST<{ needGuide: boolean; task: UserTask }>('/api/guide/getTasks', { + desktopToAppToken: getDesktopSessionFromSessionStorage()?.token + }) + +export const checkUserTask = () => + POST('/api/guide/checkTask', { + desktopToAppToken: getDesktopSessionFromSessionStorage()?.token + }) diff --git a/frontend/providers/devbox/api/template.ts b/frontend/providers/devbox/api/template.ts new file mode 100644 index 00000000000..454568a3fc2 --- /dev/null +++ b/frontend/providers/devbox/api/template.ts @@ -0,0 +1,132 @@ +import { Tag, TemplateRepositoryKind } from "@/prisma/generated/client"; +import { DELETE, GET, POST } from "@/services/request"; +import { CreateTemplateRepositoryType, UpdateTemplateRepositoryType, UpdateTemplateType } from "@/utils/vaildate"; + +export const listOfficialTemplateRepository = () => GET<{ + templateRepositoryList: { + uid: string; + name: string; + kind: TemplateRepositoryKind; + iconId: string; + description: string | null; + }[] +}>(`/api/templateRepository/listOfficial`) +export const listTemplateRepository = (page: { + page: number, + pageSize: number, +}, tags?: string[], search?: string) => { + const searchParams = new URLSearchParams() + if (tags && tags.length > 0) { + tags.forEach((tag) => { + searchParams.append('tags', tag) + }) + } + searchParams.append('page', page.page.toString()) + searchParams.append('pageSize', page.pageSize.toString()) + if (search) searchParams.append('search', search) + return GET<{ + templateRepositoryList: { + uid: string; + name: string; + description: string | null; + iconId: string | null; + templates: { + uid: string; + name: string; + }[]; + templateRepositoryTags: { + tag: Tag; + }[]; + }[], + page: { + page: number, + pageSize: number, + totalItems: number, + totalPage: number, + } + }>(`/api/templateRepository/list?${searchParams.toString()}`) + +} +export const listPrivateTemplateRepository = ({ + search, + page, + pageSize, +}: { + search?: string, + page?: number, + pageSize?: number, +} = {}) => { + const searchParams = new URLSearchParams() + + if (search) searchParams.append('search', search) + if (page) searchParams.append('page', page.toString()) + if (pageSize) searchParams.append('pageSize', pageSize.toString()) + return GET<{ + templateRepositoryList: { + uid: string; + name: string; + description: string | null; + iconId: string | null; + templates: { + uid: string; + name: string; + }[]; + isPublic: boolean; + templateRepositoryTags: { + tag: Tag; + }[]; + }[], + page: { + page: number, + pageSize: number, + totalItems: number, + totalPage: number, + } + }>(`/api/templateRepository/listPrivate?${searchParams.toString()}`) +} + +export const getTemplateRepository = (uid: string) => GET<{ + templateRepository: { + templates: { + name: string; + uid: string; + }[]; + uid: string; + isPublic: true; + name: string; + description: string | null; + iconId: string | null; + templateRepositoryTags: { + tag: Tag; + }[]; + } +}>(`/api/templateRepository/get?uid=${uid}`) +export const getTemplateConfig = (uid: string) => GET<{ + template: { + name: string; + uid: string; + config: string; + } +}>(`/api/templateRepository/template/getConfig?uid=${uid}`) +export const listTemplate = (templateRepositoryUid: string) => GET<{ + templateList: { + uid: string; + name: string; + config: string; + image: string; + createAt: Date; + updateAt: Date; + }[] +}>(`/api/templateRepository/template/list?templateRepositoryUid=${templateRepositoryUid}`) +export const listTag = () => GET<{ + tagList: Tag[] +}>(`/api/templateRepository/tag/list`) + +export const createTemplateReposistory = (data: CreateTemplateRepositoryType) => POST(`/api/templateRepository/withTemplate/create`, data) +export const initUser = () => POST(`/api/auth/init`) + +export const deleteTemplateRepository = (templateRepositoryUid: string) => DELETE(`/api/templateRepository/delete?templateRepositoryUid=${templateRepositoryUid}`) + +export const updateTemplateReposistory = (data: UpdateTemplateRepositoryType) => POST(`/api/templateRepository/update`, data) +export const updateTemplate = (data: UpdateTemplateType) => POST(`/api/templateRepository/withTemplate/update`, data) +export const deleteTemplate = (templateUid: string) => DELETE(`/api/templateRepository/template/delete?uid=${templateUid}`) \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxHeader.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxHeader.tsx new file mode 100644 index 00000000000..46ad42d6011 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxHeader.tsx @@ -0,0 +1,90 @@ +import MyIcon from "@/components/Icon"; +import { TemplateState } from "@/constants/template"; +import { usePathname, useRouter } from "@/i18n"; +import { useTemplateStore } from "@/stores/template"; +import { Box, Button, Center, Flex, Text, useTheme } from "@chakra-ui/react"; +import { useTranslations } from "next-intl"; +import { useEffect } from "react"; + +export default function DevboxHeader({ listLength }: { listLength: number }) { + const { openTemplateModal, config, updateTemplateModalConfig } = useTemplateStore() + const theme = useTheme() + const router = useRouter() + const t = useTranslations() + const pathname = usePathname() + const lastRoute = '/?openTemplate=publicTemplate' + useEffect(() => { + const refreshLastRoute = '/' + if (config.lastRoute.includes('openTemplate')) { + openTemplateModal({ + ...config, + lastRoute: refreshLastRoute + }) + } else { + updateTemplateModalConfig({ + ...config, + lastRoute: refreshLastRoute + }) + } + }, []) + return +
+ +
+ + {t('devbox_list')} + + + ( {listLength} ) + + { + // setLastRoute(pathname) + openTemplateModal({ + 'templateState': TemplateState.publicTemplate, + lastRoute, + }) + }} + > + + + {t("scan_templates")} + + + +
; +} \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxList.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxList.tsx index ff37ed8b9f8..0264056cc81 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxList.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxList.tsx @@ -1,20 +1,20 @@ -import dynamic from 'next/dynamic' +import { Box, Button, Flex, Image, MenuButton, Text } from '@chakra-ui/react' +import { MyTable, SealosMenu, useMessage } from '@sealos/ui' import { useTranslations } from 'next-intl' +import dynamic from 'next/dynamic' import { useCallback, useState } from 'react' import { sealosApp } from 'sealos-desktop-sdk/app' -import { SealosMenu, MyTable, useMessage } from '@sealos/ui' -import { Box, Button, Center, Flex, Image, MenuButton, useTheme, Text } from '@chakra-ui/react' +import { pauseDevbox, restartDevbox, startDevbox } from '@/api/devbox' import { useRouter } from '@/i18n' import { useGlobalStore } from '@/stores/global' -import { DevboxListItemType } from '@/types/devbox' -import { pauseDevbox, restartDevbox, startDevbox } from '@/api/devbox' +import { DevboxListItemTypeV2 } from '@/types/devbox' +import DevboxStatusTag from '@/components/DevboxStatusTag' import MyIcon from '@/components/Icon' import IDEButton from '@/components/IDEButton' -import PodLineChart from '@/components/PodLineChart' -import DevboxStatusTag from '@/components/DevboxStatusTag' import ReleaseModal from '@/components/modals/releaseModal' +import PodLineChart from '@/components/PodLineChart' const DelModal = dynamic(() => import('@/components/modals/DelModal')) @@ -22,10 +22,9 @@ const DevboxList = ({ devboxList = [], refetchDevboxList }: { - devboxList: DevboxListItemType[] + devboxList: DevboxListItemTypeV2[] refetchDevboxList: () => void }) => { - const theme = useTheme() const router = useRouter() const t = useTranslations() const { message: toast } = useMessage() @@ -34,17 +33,17 @@ const DevboxList = ({ const { setLoading } = useGlobalStore() const [onOpenRelease, setOnOpenRelease] = useState(false) - const [delDevbox, setDelDevbox] = useState(null) - const [currentDevboxListItem, setCurrentDevboxListItem] = useState( + const [delDevbox, setDelDevbox] = useState(null) + const [currentDevboxListItem, setCurrentDevboxListItem] = useState( null ) - const handleOpenRelease = (devbox: DevboxListItemType) => { + const handleOpenRelease = (devbox: DevboxListItemTypeV2) => { setCurrentDevboxListItem(devbox) setOnOpenRelease(true) } const handlePauseDevbox = useCallback( - async (devbox: DevboxListItemType) => { + async (devbox: DevboxListItemTypeV2) => { try { setLoading(true) await pauseDevbox({ devboxName: devbox.name }) @@ -65,7 +64,7 @@ const DevboxList = ({ [refetchDevboxList, setLoading, t, toast] ) const handleRestartDevbox = useCallback( - async (devbox: DevboxListItemType) => { + async (devbox: DevboxListItemTypeV2) => { try { setLoading(true) await restartDevbox({ devboxName: devbox.name }) @@ -86,7 +85,7 @@ const DevboxList = ({ [refetchDevboxList, setLoading, t, toast] ) const handleStartDevbox = useCallback( - async (devbox: DevboxListItemType) => { + async (devbox: DevboxListItemTypeV2) => { try { setLoading(true) await startDevbox({ devboxName: devbox.name }) @@ -107,7 +106,7 @@ const DevboxList = ({ [refetchDevboxList, setLoading, t, toast] ) const handleGoToTerminal = useCallback( - async (devbox: DevboxListItemType) => { + async (devbox: DevboxListItemTypeV2) => { const defaultCommand = `kubectl exec -it $(kubectl get po -l app.kubernetes.io/name=${devbox.name} -oname) -- sh -c "clear; (bash || ash || sh)"` try { sealosApp.runEvents('openDesktopApp', { @@ -127,27 +126,23 @@ const DevboxList = ({ }, [t, toast] ) - const columns: { title: string - dataIndex?: keyof DevboxListItemType + dataIndex?: keyof DevboxListItemTypeV2 key: string - render?: (item: DevboxListItemType) => JSX.Element + render?: (item: DevboxListItemTypeV2) => JSX.Element }[] = [ { title: t('name'), key: 'name', - render: (item: DevboxListItemType) => { + render: (item) => { return ( { - e.currentTarget.src = '/images/custom.svg' - }} + src={`/images/${item.template.templateRepository.iconId}.svg`} /> {item.name} @@ -159,22 +154,22 @@ const DevboxList = ({ { title: t('status'), key: 'status', - render: (item: DevboxListItemType) => + render: (item) => }, { title: t('create_time'), dataIndex: 'createTime', key: 'createTime', - render: (item: DevboxListItemType) => { + render: (item) => { return {item.createTime} } }, { title: t('cpu'), key: 'cpu', - render: (item: DevboxListItemType) => ( + render: (item) => ( - + ( + render: (item) => ( - + ( + render: (item) => ( + } @@ -344,35 +338,8 @@ const DevboxList = ({ ) } ] - return ( - - -
- -
- - {t('devbox_list')} - - - ( {devboxList.length} ) - - - -
+ <> {!!delDevbox && ( )} -
+ ) } diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxListContainer.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxListContainer.tsx new file mode 100644 index 00000000000..3f0f3d90937 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxListContainer.tsx @@ -0,0 +1,109 @@ +// components/DevboxListContainer.tsx +'use client' +import { useDevboxStore } from '@/stores/devbox' +import { useTemplateStore } from '@/stores/template' +import { DevboxListItemTypeV2 } from '@/types/devbox' +import { isElementInViewport } from '@/utils/tools' +import { Flex, FlexProps } from '@chakra-ui/react' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { useRouter } from '@/i18n' +import { useCallback, useEffect, useRef } from 'react' +import DevboxHeader from './DevboxHeader' +import DevboxList from './DevboxList' +import Empty from './Empty' +import useListDriver from '@/hooks/useListDriver' + +function useDevboxList() { + const queryClient = useQueryClient() + const router = useRouter() + const { devboxList, setDevboxList, loadAvgMonitorData, intervalLoadPods } = useDevboxStore() + const { isOpen: templateIsOpen } = useTemplateStore() + const list = useRef(devboxList) + + const { isLoading, refetch: refetchDevboxList } = useQuery(['devboxListQuery'], setDevboxList, { + onSettled(res) { + if (!res) return + // refreshList(res) + list.current = res + }, + refetchInterval: !templateIsOpen ? 3000 : false, + staleTime: 3000, + enabled: !templateIsOpen + }) + const getViewportDevboxes = (minCount = 3) => { + const doms = document.querySelectorAll('.devboxListItem') + const viewportDomIds = Array.from(doms) + .filter(isElementInViewport) + .map((item) => item.getAttribute('data-id')) + + return viewportDomIds.length < minCount + ? devboxList + : devboxList.filter((devbox) => viewportDomIds.includes(devbox.id)) + } + useQuery( + ['intervalLoadPods', devboxList.length], + () => { + const viewportDevboxList = getViewportDevboxes() + return viewportDevboxList + .filter((devbox) => devbox.status.value !== 'Stopped') + .map((devbox) => intervalLoadPods(devbox.name, false)) + }, + { + refetchOnMount: true, + refetchInterval: !templateIsOpen ? 3000 : false, + staleTime: 3000, + enabled: !isLoading && !templateIsOpen + } + ) + + useQuery( + ['loadAvgMonitorData', devboxList.length], + () => { + const viewportDevboxList = getViewportDevboxes() + return viewportDevboxList + .filter((devbox) => devbox.status.value === 'Running') + .map((devbox) => loadAvgMonitorData(devbox.name)) + }, + { + refetchInterval: !templateIsOpen ? 2 * 60 * 1000 : false, + staleTime: 2 * 60 * 1000, + enabled: !isLoading && !templateIsOpen + } + ) + // 路由预加载 + useEffect(() => { + router.prefetch('/devbox/detail') + router.prefetch('/devbox/create') + }, [router]) + + return { + list: list.current, + isLoading, + refetchList: useCallback(() => { + queryClient.invalidateQueries(['devboxListQuery']) + }, [queryClient]) + } +} + +export default function DevboxListContainer({ ...props }: FlexProps) { + const { list, isLoading, refetchList } = useDevboxList() + const { handleUserGuide } = useListDriver() + + useEffect(() => { + if (list.length > 0) { + handleUserGuide() + } + }, [list.length]) + + return ( + + + {list.length === 0 && !isLoading ? ( + + ) : ( + + )} + {/* */} + + ) +} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/Empty.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/Empty.tsx index 5aa415a7da4..512eaadd1d2 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/Empty.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/Empty.tsx @@ -1,34 +1,24 @@ +import { Box, Flex } from '@chakra-ui/react' import { useTranslations } from 'next-intl' -import { Button, Box } from '@chakra-ui/react' -import { useRouter } from '@/i18n' import MyIcon from '@/components/Icon' - -import styles from './empty.module.scss' +import { useRouter } from '@/i18n' const Empty = () => { const router = useRouter() const t = useTranslations() - return ( - {t('devbox_empty')} - - +
) } diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/page.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/page.tsx index 5e852d40690..0e7c9b21cf5 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/(home)/page.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/page.tsx @@ -1,131 +1,5 @@ -'use client' +import DevboxListContainer from './components/DevboxListContainer' -import { useRouter } from '@/i18n' -import { useQuery } from '@tanstack/react-query' -import { useCallback, useEffect, useRef, useState } from 'react' - -import Empty from './components/Empty' -import DevboxList from './components/DevboxList' - -import { useLoading } from '@/hooks/useLoading' -import { useDevboxStore } from '@/stores/devbox' -import { isElementInViewport } from '@/utils/tools' -import { DevboxListItemType } from '@/types/devbox' - -const EmptyPage = () => { - const router = useRouter() - const { Loading } = useLoading() - const { devboxList, setDevboxList, loadAvgMonitorData, intervalLoadPods } = useDevboxStore() - - const [_, setFresh] = useState(false) - const list = useRef(devboxList) - - const refreshList = useCallback( - (res = devboxList) => { - list.current = res - setFresh((state) => !state) - return null - }, - [devboxList] - ) - - const { isLoading, refetch: refetchDevboxList } = useQuery(['devboxListQuery'], setDevboxList, { - onSettled(res) { - if (!res) return - refreshList(res) - } - }) - - useQuery( - ['intervalLoadPods', devboxList.length], - () => { - const doms = document.querySelectorAll(`.devboxListItem`) - const viewportDomIds = Array.from(doms) - .filter((item) => isElementInViewport(item)) - .map((item) => item.getAttribute('data-id')) - - const viewportDevboxList = - viewportDomIds.length < 3 - ? devboxList - : devboxList.filter((devbox) => viewportDomIds.includes(devbox.id)) - - return viewportDevboxList - .filter((devbox) => devbox.status.value !== 'Stopped') - .map((devbox) => intervalLoadPods(devbox.name, false)) - }, - { - refetchOnMount: true, - refetchInterval: 3000, - onSettled() { - refreshList() - } - } - ) - - useQuery( - ['refresh'], - () => { - refreshList() - return null - }, - { - refetchInterval: 3000 - } - ) - - const { refetch: refetchAvgMonitorData } = useQuery( - ['loadAvgMonitorData', devboxList.length], - () => { - const doms = document.querySelectorAll('.devboxListItem') - const viewportDomIds = Array.from(doms) - .filter((dom) => isElementInViewport(dom)) - .map((dom) => dom.getAttribute('data-id')) - - const viewportDevboxList = - viewportDomIds.length < 3 - ? devboxList - : devboxList.filter((devbox) => viewportDomIds.includes(devbox.id)) - - // TODO: reference applaunchpad to request rhythmically - return viewportDevboxList - .filter((devbox) => devbox.status.value === 'Running') - .map((devbox) => loadAvgMonitorData(devbox.name)) - }, - { - refetchOnMount: true, - refetchInterval: 2 * 60 * 1000, - onError(err) { - console.log(err) - }, - onSettled() { - refreshList() - } - } - ) - - useEffect(() => { - // router.prefetch('/devbox/detail') - router.prefetch('/devbox/create') - }, [router]) - - return ( - <> - {devboxList.length === 0 && !isLoading ? ( - - ) : ( - <> - { - refetchDevboxList() - refetchAvgMonitorData() - }} - /> - - )} - - - ) +export default async function EmptyPage() { + return } - -export default EmptyPage diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/Form.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/Form.tsx deleted file mode 100644 index 15142e8adcf..00000000000 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/Form.tsx +++ /dev/null @@ -1,854 +0,0 @@ -'use client' - -import { - Box, - Button, - Center, - Flex, - FormControl, - Grid, - IconButton, - Image, - Input, - Switch, - Text, - useTheme -} from '@chakra-ui/react' -import { throttle } from 'lodash' -import dynamic from 'next/dynamic' -import { customAlphabet } from 'nanoid' -import { useEffect, useState } from 'react' -import { useTranslations } from 'next-intl' -import { UseFormReturn, useFieldArray } from 'react-hook-form' -import { MySelect, MySlider, Tabs, useMessage } from '@sealos/ui' - -import { useRouter } from '@/i18n' -import MyIcon from '@/components/Icon' -import PriceBox from '@/components/PriceBox' -import QuotaBox from '@/components/QuotaBox' - -import { useEnvStore } from '@/stores/env' -import { useDevboxStore } from '@/stores/devbox' -import { useRuntimeStore } from '@/stores/runtime' - -import { ProtocolList } from '@/constants/devbox' -import type { DevboxEditType } from '@/types/devbox' -import { obj2Query } from '@/utils/tools' -import { CpuSlideMarkList, MemorySlideMarkList } from '@/constants/devbox' - -const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 12) - -export type CustomAccessModalParams = { - publicDomain: string - customDomain: string -} - -const CustomAccessModal = dynamic(() => import('@/components/modals/CustomAccessModal')) - -const Form = ({ - formHook, - pxVal, - isEdit -}: { - formHook: UseFormReturn - pxVal: number - isEdit: boolean -}) => { - const theme = useTheme() - const router = useRouter() - const t = useTranslations() - const { - control, - register, - setValue, - getValues, - formState: { errors } - } = formHook - const { - fields: networks, - append: appendNetworks, - remove: removeNetworks, - update: updateNetworks - } = useFieldArray({ - control, - name: 'networks' - }) - - const { - languageVersionMap, - frameworkVersionMap, - osVersionMap, - languageTypeList, - frameworkTypeList, - osTypeList, - getRuntimeVersionList, - getRuntimeVersionDefault, - getRuntimeDetailLabel - } = useRuntimeStore() - const { env } = useEnvStore() - - const [customAccessModalData, setCustomAccessModalData] = useState() - const navList: { id: string; label: string; icon: string }[] = [ - { - id: 'baseInfo', - label: t('basic_configuration'), - icon: 'formInfo' - }, - { - id: 'network', - label: t('Network Configuration'), - icon: 'network' - } - ] - const { message: toast } = useMessage() - const [activeNav, setActiveNav] = useState(navList[0].id) - const { devboxList } = useDevboxStore() - - // listen scroll and set activeNav - useEffect(() => { - const scrollFn = throttle((e: Event) => { - if (!e.target) return - const doms = navList.map((item) => ({ - dom: document.getElementById(item.id), - id: item.id - })) - - const dom = e.target as HTMLDivElement - const scrollTop = dom.scrollTop - - for (let i = doms.length - 1; i >= 0; i--) { - const offsetTop = doms[i].dom?.offsetTop || 0 - if (scrollTop + 500 >= offsetTop) { - setActiveNav(doms[i].id) - break - } - } - }, 200) - document.getElementById('form-container')?.addEventListener('scroll', scrollFn) - return () => { - document.getElementById('form-container')?.removeEventListener('scroll', scrollFn) - } - // eslint-disable-next-line - }, []) - - if (!formHook) return null - - const Label = ({ - children, - w = 'auto', - ...props - }: { - children: string - w?: number | 'auto' - [key: string]: any - }) => ( - - {children} - - ) - - const boxStyles = { - border: theme.borders.base, - borderRadius: 'lg', - mb: 4, - bg: 'white' - } - - const headerStyles = { - py: 4, - pl: '42px', - borderTopRadius: 'lg', - fontSize: 'xl', - color: 'grayModern.900', - fontWeight: 'bold', - display: 'flex', - alignItems: 'center', - backgroundColor: 'grayModern.50' - } - - return ( - <> - - {/* left sidebar */} - - - router.replace( - `/devbox/create?${obj2Query({ - type: 'yaml' - })}` - ) - } - /> - - {navList.map((item) => ( - { - setActiveNav(item.id) - window.location.hash = item.id - }}> - - - - {item?.label} - - - ))} - - - - - - - - - {/* right content */} - - {/* base info */} - - - - {t('basic_configuration')} - - - {/* Devbox Name */} - - - - - /^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?)*$/.test( - value - ) || t('devbox_name_invalid') - } - })} - onBlur={(e) => { - const lowercaseValue = e.target.value.toLowerCase() - - setValue('name', lowercaseValue) - const networks = getValues('networks') - networks.forEach((network, i) => { - updateNetworks(i, { - ...network, - networkName: `${lowercaseValue}-${nanoid()}` - }) - }) - }} - /> - - - {/* Runtime Type */} - - - - {/* Language */} - {languageTypeList.length !== 0 && {t('language')}} - - {languageTypeList && - languageTypeList?.map((item) => { - return ( -
{ - if (isEdit) return - const devboxName = getValues('name') - if (!devboxName) { - toast({ - title: t('Please enter the devbox name first'), - status: 'warning' - }) - return - } - setValue('runtimeType', item.id) - setValue( - 'runtimeVersion', - languageVersionMap[getValues('runtimeType')][0].id - ) - setValue( - 'networks', - languageVersionMap[getValues('runtimeType')][0].defaultPorts.map( - (port) => ({ - networkName: `${devboxName}-${nanoid()}`, - portName: nanoid(), - port: port, - protocol: 'HTTP', - openPublicDomain: true, - publicDomain: `${nanoid()}.${env.ingressDomain}`, - customDomain: '' - }) - ) - ) - }}> - { - e.currentTarget.src = '/images/custom.svg' - }} - /> - - {item?.label} - -
- ) - })} -
- {/* framework */} - {frameworkTypeList.length !== 0 && {t('framework')}} - - {frameworkTypeList && - frameworkTypeList?.map((item) => { - return ( -
{ - if (isEdit) return - const devboxName = getValues('name') - if (!devboxName) { - toast({ - title: t('Please enter the devbox name first'), - status: 'warning' - }) - return - } - setValue('runtimeType', item.id) - setValue( - 'runtimeVersion', - frameworkVersionMap[getValues('runtimeType')][0].id - ) - setValue( - 'networks', - frameworkVersionMap[getValues('runtimeType')][0].defaultPorts.map( - (port) => ({ - networkName: `${devboxName}-${nanoid()}`, - portName: nanoid(), - port: port, - protocol: 'HTTP', - openPublicDomain: true, - publicDomain: `${nanoid()}.${env.ingressDomain}`, - customDomain: '' - }) - ) - ) - }}> - { - e.currentTarget.src = '/images/custom.svg' - }} - /> - - {item?.label} - -
- ) - })} -
- {/* os */} - {osTypeList.length !== 0 && {t('os')}} - - {osTypeList && - osTypeList?.map((item) => { - return ( -
{ - if (isEdit) return - const devboxName = getValues('name') - if (!devboxName) { - toast({ - title: t('Please enter the devbox name first'), - status: 'warning' - }) - return - } - setValue('runtimeType', item.id) - setValue( - 'runtimeVersion', - osVersionMap[getValues('runtimeType')][0].id - ) - setValue( - 'networks', - osVersionMap[getValues('runtimeType')][0].defaultPorts.map( - (port) => ({ - networkName: `${devboxName}-${nanoid()}`, - portName: nanoid(), - port: port, - protocol: 'HTTP', - openPublicDomain: true, - publicDomain: `${nanoid()}.${env.ingressDomain}`, - customDomain: '' - }) - ) - ) - }}> - { - e.currentTarget.src = '/images/custom.svg' - }} - /> - - {item?.label} - -
- ) - })} -
-
-
- {/* Runtime Version */} - - - {isEdit ? ( - - ) : ( - { - if (isEdit) return - const devboxName = getValues('name') - if (!devboxName) { - toast({ - title: t('Please enter the devbox name first'), - status: 'warning' - }) - return - } - setValue('runtimeVersion', val) - setValue( - 'networks', - getRuntimeVersionList(getValues('runtimeType'))[0].defaultPorts.map( - (port) => ({ - networkName: `${devboxName}-${nanoid()}`, - portName: nanoid(), - port: port, - protocol: 'HTTP', - openPublicDomain: true, - publicDomain: `${nanoid()}.${env.ingressDomain}`, - customDomain: '' - }) - ) - ) - }} - /> - )} - - {/* CPU */} - - - { - setValue('cpu', CpuSlideMarkList[e].value) - }} - max={CpuSlideMarkList.length - 1} - min={0} - step={1} - /> - - {t('core')} - - - {/* Memory */} - - - { - setValue('memory', MemorySlideMarkList[e].value) - }} - max={MemorySlideMarkList.length - 1} - min={0} - step={1} - /> - -
-
- {/* network */} - - - - {t('Network Configuration')} - - - {networks.length === 0 && ( - - )} - {networks.map((network, i) => ( - - - - {t('Container Port')} - - - {i === networks.length - 1 && networks.length < 5 && ( - - - - )} - - - - {t('Open Public Access')} - - - { - const devboxName = getValues('name') - if (!devboxName) { - toast({ - title: t('Please enter the devbox name first'), - status: 'warning' - }) - return - } - - updateNetworks(i, { - ...getValues('networks')[i], - networkName: network.networkName || `${devboxName}-${nanoid()}`, - protocol: network.protocol || 'HTTP', - openPublicDomain: e.target.checked, - publicDomain: network.publicDomain || `${nanoid()}.${env.ingressDomain}` - }) - }} - /> - - - {network.openPublicDomain && ( - <> - - - - { - updateNetworks(i, { - ...getValues('networks')[i], - protocol: val - }) - }} - /> - - - {network.customDomain ? network.customDomain : network.publicDomain} - - - setCustomAccessModalData({ - publicDomain: network.publicDomain, - customDomain: network.customDomain - }) - }> - {t('Custom Domain')} - - - - - - )} - {networks.length >= 1 && ( - - - } - onClick={() => removeNetworks(i)} - /> - - )} - - ))} - - - {!!customAccessModalData && ( - setCustomAccessModalData(undefined)} - onSuccess={(e) => { - const i = networks.findIndex( - (item) => item.publicDomain === customAccessModalData.publicDomain - ) - if (i === -1) return - updateNetworks(i, { - ...networks[i], - customDomain: e - }) - - setCustomAccessModalData(undefined) - }} - /> - )} -
-
- - ) -} - -export default Form diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/Header.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/Header.tsx index 8b3b97a83ef..d58728a90e8 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/Header.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/Header.tsx @@ -1,14 +1,16 @@ -import JSZip from 'jszip' +import { Box, Button, Flex } from '@chakra-ui/react' import dayjs from 'dayjs' -import React, { useCallback } from 'react' +import JSZip from 'jszip' import { useTranslations } from 'next-intl' -import { Box, Flex, Button } from '@chakra-ui/react' +import { useCallback } from 'react' -import { useRouter } from '@/i18n' import MyIcon from '@/components/Icon' -import { downLoadBlob } from '@/utils/tools' +import { useRouter } from '@/i18n' +import { useEnvStore } from '@/stores/env' import { useGlobalStore } from '@/stores/global' +import { useTemplateStore } from '@/stores/template' import type { YamlItemType } from '@/types/index' +import { downLoadBlob } from '@/utils/tools' const Header = ({ title, @@ -24,7 +26,8 @@ const Header = ({ const router = useRouter() const { lastRoute } = useGlobalStore() const t = useTranslations() - + const { config } = useTemplateStore() + const { env } = useEnvStore() const handleExportYaml = useCallback(async () => { const zip = new JSZip() yamlList.forEach((item) => { @@ -33,10 +36,15 @@ const Header = ({ const res = await zip.generateAsync({ type: 'blob' }) downLoadBlob(res, 'application/zip', `yaml${dayjs().format('YYYYMMDDHHmmss')}.zip`) }, [yamlList]) - return ( - router.replace(lastRoute)}> + { + if (config.lastRoute) { + router.replace(lastRoute) + } else { + router.replace(lastRoute) + }} + }> {t(title)} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/CpuSelector.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/CpuSelector.tsx new file mode 100644 index 00000000000..fa527cb89c2 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/CpuSelector.tsx @@ -0,0 +1,28 @@ +import { CpuSlideMarkList } from "@/constants/devbox" +import { DevboxEditTypeV2 } from "@/types/devbox" +import { Box, Flex, FlexProps } from "@chakra-ui/react" +import { MySlider } from "@sealos/ui" +import { useTranslations } from "next-intl" +import { useFormContext } from "react-hook-form" +import Label from "../Label" + +export default function CpuSelector(props: FlexProps) { + const t = useTranslations() + const { watch, setValue } = useFormContext() + return + + { + setValue('cpu', CpuSlideMarkList[e].value) + }} + max={CpuSlideMarkList.length - 1} + min={0} + step={1} + /> + + {t('core')} + + +} \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/DevboxNameInput.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/DevboxNameInput.tsx new file mode 100644 index 00000000000..a4b7084dc81 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/DevboxNameInput.tsx @@ -0,0 +1,56 @@ +import { DevboxEditTypeV2 } from "@/types/devbox" +import { nanoid } from "@/utils/tools" +import { devboxNameSchema } from "@/utils/vaildate" +import { Flex, FormControl, FormControlProps, Input } from "@chakra-ui/react" +import { useTranslations } from "next-intl" +import { useFieldArray, useFormContext } from "react-hook-form" +import Label from "../Label" + +export default function DevboxNameInput({isEdit, ...props}: {isEdit: boolean} & FormControlProps) { + const t = useTranslations() + const { + register, + setValue, + formState: { errors }, + control + } = useFormContext() + const { + fields: networks, + update: updateNetworks + } = useFieldArray({ + control, + name: 'networks' + }) + return + + + + devboxNameSchema.safeParse(value).success || t('devbox_name_invalid') + } + })} + onBlur={(e) => { + const lowercaseValue = e.target.value.toLowerCase() + setValue('name', lowercaseValue) + networks.forEach((network, i) => { + updateNetworks(i, { + ...network, + networkName: `${lowercaseValue}-${nanoid()}` + }) + }) + }} + /> + + +} \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/MemorySelector.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/MemorySelector.tsx new file mode 100644 index 00000000000..3af3ced3037 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/MemorySelector.tsx @@ -0,0 +1,25 @@ +import { MemorySlideMarkList } from "@/constants/devbox" +import { DevboxEditTypeV2 } from "@/types/devbox" +import { Flex, FlexProps } from "@chakra-ui/react" +import { MySlider } from "@sealos/ui" +import { useTranslations } from "next-intl" +import { useFormContext } from "react-hook-form" +import Label from "../Label" + +export default function MemorySelector(props: FlexProps) { + const t = useTranslations() + const { watch, setValue } = useFormContext() + return + + { + setValue('memory', MemorySlideMarkList[e].value) + }} + max={MemorySlideMarkList.length - 1} + min={0} + step={1} + /> + +} \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositoryListNav.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositoryListNav.tsx new file mode 100644 index 00000000000..18a9a22629d --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositoryListNav.tsx @@ -0,0 +1,83 @@ +import MyIcon from '@/components/Icon' +import { TemplateState } from '@/constants/template' +import { useTemplateStore } from '@/stores/template' +import { Box, Flex, Text } from '@chakra-ui/react' +import { useTranslations } from 'next-intl' +import { usePathname } from '@/i18n' + +const TemplateRepositoryListNav = () => { + const t = useTranslations() + const { openTemplateModal, config, isOpen } = useTemplateStore() + const lastRoute = usePathname() + return ( + + {/* All Templates Tab */} + { + openTemplateModal({ + templateState: TemplateState.publicTemplate, + lastRoute + }) + }}> + + + {t('all_templates')} + + + + {/* Divider */} + + + {/* My Templates Tab */} + { + openTemplateModal({ + templateState: TemplateState.privateTemplate, + lastRoute + }) + }}> + + + {t('my_templates')} + + + + ) +} + +export default TemplateRepositoryListNav diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/TemplateReposistoryItem.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/TemplateReposistoryItem.tsx new file mode 100644 index 00000000000..05ef402d9c6 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/TemplateReposistoryItem.tsx @@ -0,0 +1,68 @@ +import { useDevboxStore } from "@/stores/devbox"; +import { DevboxEditTypeV2 } from "@/types/devbox"; +import { Center, Img, Text } from "@chakra-ui/react"; +import { useMessage } from "@sealos/ui"; +import { useTranslations } from "next-intl"; +import { useFormContext } from "react-hook-form"; + +export default function TemplateRepositoryItem({ item, isEdit }: { item: { uid: string, iconId: string, name: string }; isEdit: boolean}) { + const { message: toast } = useMessage() + const t = useTranslations() + const { getValues, setValue, watch } = useFormContext() + const { startedTemplate, setStartedTemplate } = useDevboxStore() + return
{ + if (isEdit) return + const devboxName = getValues('name') + if (!devboxName) { + toast({ + title: t('Please enter the devbox name first'), + status: 'warning' + }) + return + } + if (startedTemplate && startedTemplate.uid !== item.uid) { + setStartedTemplate(undefined) + } + setValue('templateRepositoryUid', item.uid) + }} + > + {item.uid} + + {item.name} + +
+} \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx new file mode 100644 index 00000000000..a233868741e --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx @@ -0,0 +1,195 @@ +import { getTemplateRepository, listOfficialTemplateRepository } from '@/api/template' +import useDriver from '@/hooks/useDriver' +import { TemplateRepositoryKind } from '@/prisma/generated/client' +import { useDevboxStore } from '@/stores/devbox' +import { DevboxEditTypeV2 } from '@/types/devbox' +import { TemplateRepository } from '@/types/template' +import { Box, Flex, VStack } from '@chakra-ui/react' +import { useQuery } from '@tanstack/react-query' +import { useTranslations } from 'next-intl' +import { useEffect, useMemo } from 'react' +import { useFormContext } from 'react-hook-form' +import Label from '../../Label' +import TemplateRepositoryListNav from '../TemplateRepositoryListNav' +import TemplateRepositoryItem from './TemplateReposistoryItem' + +interface TemplateRepositorySelectorProps { + isEdit: boolean +} + +export default function TemplateRepositorySelector({ isEdit }: TemplateRepositorySelectorProps) { + const { startedTemplate, setStartedTemplate } = useDevboxStore() + const { setValue, getValues, watch } = useFormContext() + const t = useTranslations() + const { handleUserGuide } = useDriver() + + const templateRepositoryQuery = useQuery( + ['list-official-template-repository'], + listOfficialTemplateRepository, + { + onSuccess(res) { + console.log('res', res) + handleUserGuide() + }, + staleTime: Infinity, + cacheTime: 1000 * 60 * 30 + } + ) + const curTemplateRepositoryUid = watch('templateRepositoryUid') + const curTemplateRepositoryDetail = useQuery( + ['get-template-repository', curTemplateRepositoryUid], + () => { + return getTemplateRepository(curTemplateRepositoryUid) + }, + { + enabled: !!isEdit && !!curTemplateRepositoryUid + } + ) + + const templateData = useMemo( + () => templateRepositoryQuery.data?.templateRepositoryList || [], + [templateRepositoryQuery.data] + ) + + const categorizedData = useMemo(() => { + return templateData.reduce( + (acc, item) => { + acc[item.kind] = [...(acc[item.kind] || []), item] + return acc + }, + { + LANGUAGE: [], + FRAMEWORK: [], + OS: [], + CUSTOM: [] + } as Record + ) + }, [templateData]) + useEffect(() => { + if (!startedTemplate || isEdit) { + return + } + const templateUid = startedTemplate.uid + if ( + templateData.findIndex((item) => { + return item.uid === templateUid + }) > -1 + ) { + setStartedTemplate(undefined) + } + setValue('templateRepositoryUid', templateUid) + }, [startedTemplate, isEdit]) + + useEffect(() => { + if (startedTemplate || isEdit) { + return + } + if ( + !( + templateRepositoryQuery.isSuccess && + templateData.length > 0 && + templateRepositoryQuery.isFetched + ) + ) + return + const curTemplateRepositoryUid = getValues('templateRepositoryUid') + const curTemplateRepository = templateData.find((item) => { + return item.uid === curTemplateRepositoryUid + }) + if (!curTemplateRepository) { + const defaultTemplateRepositoryUid = templateData[0].uid + setValue('templateRepositoryUid', defaultTemplateRepositoryUid) + } + }, [ + templateRepositoryQuery.isSuccess, + startedTemplate, + templateData, + templateRepositoryQuery.isFetched, + isEdit + ]) + + useEffect(() => { + if ( + !isEdit || + !templateRepositoryQuery.isSuccess || + !templateData || + !curTemplateRepositoryDetail.isSuccess || + !curTemplateRepositoryDetail.data + ) { + return + } + const templateRepository = curTemplateRepositoryDetail.data.templateRepository + // setStartedTemplate(templateRepository) + setValue('templateRepositoryUid', templateRepository.uid) + + if ( + templateData.findIndex((item) => { + return item.uid === templateRepository.uid + }) === -1 + ) { + setStartedTemplate(templateRepository) + } + }, [ + curTemplateRepositoryDetail.isSuccess, + curTemplateRepositoryDetail.data, + curTemplateRepositoryDetail.isFetched, + isEdit, + templateData, + templateRepositoryQuery.isSuccess + ]) + return ( + + + + + + + + {!!startedTemplate && ( + + {t('current')} + + + + + )} + + {/* Language */} + {categorizedData['LANGUAGE'].length !== 0 && {t('language')}} + + {categorizedData['LANGUAGE']?.map((item) => ( + + ))} + + + + + {/* Framework */} + {categorizedData['FRAMEWORK'].length !== 0 && {t('framework')}} + + {categorizedData['FRAMEWORK']?.map((item) => ( + + ))} + + + + {/* OS */} + {categorizedData['OS'].length !== 0 && {t('os')}} + + {categorizedData['OS']?.map((item) => ( + + ))} + + + + ) +} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateSelector/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateSelector/index.tsx new file mode 100644 index 00000000000..1ac11c76f91 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateSelector/index.tsx @@ -0,0 +1,128 @@ +// RuntimeVersionSelector.tsx +import { listTemplate } from '@/api/template' +import { useEnvStore } from '@/stores/env' +import { DevboxEditTypeV2 } from '@/types/devbox' +import { nanoid } from '@/utils/tools' +import { Flex, Input } from '@chakra-ui/react' +import { MySelect, useMessage } from '@sealos/ui' +import { useQuery } from '@tanstack/react-query' +import { useTranslations } from 'next-intl' +import { useEffect } from 'react' +import { useController, useFormContext } from 'react-hook-form' +import { z } from 'zod' +import Label from '../../Label' + +interface TemplateSelectorProps { + isEdit: boolean +} + +export default function TemplateSelector({ + isEdit, +}: TemplateSelectorProps) { + const { getValues, setValue, watch, control } = useFormContext() + const { env } = useEnvStore() + const { message: toast } = useMessage() + const templateRepositoryUid = watch('templateRepositoryUid') + const isVaildTemplateRepositoryUid = z.string().uuid().safeParse(templateRepositoryUid).success + const templateListQuery = useQuery(['templateList', templateRepositoryUid], () => listTemplate(templateRepositoryUid), { + enabled: isVaildTemplateRepositoryUid, + }) + const templateList = (templateListQuery.data?.templateList || []) + const t = useTranslations() + // const defaultTemplateUid = watch('templateUid') + const menuList = templateList.map(v => ({ label: v.name, value: v.uid })) + // const defaultTemplate = defaultTemplateUid ? templateList.find(t => t.uid === defaultTemplateUid) : templateList[0] + + const { field } = useController({ + control, + name: 'templateUid', + rules: { + required: t('This runtime field is required') + } + }) + const afterUpdateTemplate = (uid: string) => { + const template = templateList.find(v => v.uid === uid)! + setValue( + 'templateConfig', + template.config as string + ) + setValue( + 'image', + template.image + ) + + } + const resetNetwork = () => { + const devboxName = getValues('name') + const config = getValues('templateConfig') + const parsedConfig = + JSON.parse(config as string) as { appPorts: [{ port: number, name: string, protocol: string }] } + setValue( + 'networks', + parsedConfig.appPorts.map( + ({ port }) => ({ + networkName: `${devboxName}-${nanoid()}`, + portName: nanoid(), + port: port, + protocol: 'HTTP', + openPublicDomain: true, + publicDomain: `${nanoid()}.${env.ingressDomain}`, + customDomain: '' + } as const) + ) + ) + } + useEffect(() => { + if (!templateListQuery.isSuccess || !templateList.length || !templateListQuery.isFetched) return + + const curTemplate = templateList.find(t => t.uid === field.value) + const isExist = !!curTemplate + if (!isExist) { + const defaultTemplate = templateList[0] + setValue('templateUid', defaultTemplate.uid) + afterUpdateTemplate(defaultTemplate.uid) + resetNetwork() + } else { + setValue('templateUid', curTemplate.uid) + afterUpdateTemplate(curTemplate.uid) + } + }, [templateListQuery.isSuccess, templateList, templateListQuery.isFetched, isEdit]) + return ( + + + {isEdit ? ( + t.uid === field.value)?.name} + disabled + /> + ) : ( + { + // if (isEdit) return + const devboxName = getValues('name') + if (!devboxName) { + toast({ + title: t('Please enter the devbox name first'), + status: 'warning' + }) + return + } + const oldTemplateUid = getValues('templateUid') + field.onChange(val) + afterUpdateTemplate(val) + if(oldTemplateUid !== val) resetNetwork() + }} + /> + )} + + ) +} \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/index.tsx new file mode 100644 index 00000000000..68e0a7ee4e3 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/index.tsx @@ -0,0 +1,36 @@ +import MyIcon from '@/components/Icon' +import { Box, BoxProps } from '@chakra-ui/react' +import { useTranslations } from 'next-intl' +import ConfigurationHeader from '../ConfigurationHeader' +import CpuSelector from './CpuSelector' +import DevboxNameInput from './DevboxNameInput' +import MemorySelector from './MemorySelector' +import TemplateRepositorySelector from './TemplateRepositorySelector' +import TemplateSelector from './TemplateSelector' + +export default function BasicConfiguration({ isEdit, ...props }: BoxProps & { isEdit: boolean }) { + const t = useTranslations() + return ( + + + + {t('basic_configuration')} + + + {/* Devbox Name */} + + {/* Template Repository */} + + {/* Runtime Version */} + + + + {/* CPU */} + + {/* Memory */} + + + + + ) +} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/ConfigurationHeader.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/ConfigurationHeader.tsx new file mode 100644 index 00000000000..d38d46c5e1b --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/ConfigurationHeader.tsx @@ -0,0 +1,19 @@ +import { Box, BoxProps } from "@chakra-ui/react"; +import { ReactNode } from "react"; + +export default function ConfigurationHeader({ children }: { children: ReactNode }) { + const headerStyles: BoxProps = { + py: 4, + pl: '42px', + borderTopRadius: 'lg', + fontSize: 'xl', + color: 'grayModern.900', + fontWeight: 'bold', + display: 'flex', + alignItems: 'center', + backgroundColor: 'grayModern.50' + } + return + {children} + +} \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/Label.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/Label.tsx new file mode 100644 index 00000000000..1804caecf39 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/Label.tsx @@ -0,0 +1,20 @@ +import { Box, BoxProps } from "@chakra-ui/react" + +const Label = ({ + children, + w = 'auto', + ...props +}: { + children: string + w?: number | 'auto' +} & BoxProps) => ( + + {children} + +) +export default Label \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/NetworkConfiguration/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/NetworkConfiguration/index.tsx new file mode 100644 index 00000000000..b4bbcfbe21b --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/NetworkConfiguration/index.tsx @@ -0,0 +1,250 @@ +import MyIcon from '@/components/Icon' +import { ProtocolList } from '@/constants/devbox' +import { useEnvStore } from '@/stores/env' +import { DevboxEditTypeV2 } from '@/types/devbox' +import { nanoid } from '@/utils/tools' +import { + Box, + BoxProps, + Button, + ButtonProps, + Flex, + IconButton, + Input, + Switch, + useTheme +} from '@chakra-ui/react' +import { MySelect, useMessage } from '@sealos/ui' +import { useTranslations } from 'next-intl' +import dynamic from 'next/dynamic' +import { useState } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import ConfigurationHeader from '../ConfigurationHeader' +// const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 12) + +export type CustomAccessModalParams = { + publicDomain: string + customDomain: string +} + +const CustomAccessModal = dynamic(() => import('@/components/modals/CustomAccessModal')) +const AppendNetworksButton = (props: ButtonProps) => { + const t = useTranslations() + return ( + + ) +} +export default function NetworkConfiguration({ isEdit, ...props }: BoxProps & { isEdit: boolean }) { + const { register, getValues, control } = useFormContext() + const theme = useTheme() + const [customAccessModalData, setCustomAccessModalData] = useState() + const { env } = useEnvStore() + const { + fields: networks, + update: updateNetworks, + append: _appendNetworks, + remove: removeNetworks + } = useFieldArray({ + control, + name: 'networks' + }) + const t = useTranslations() + const { message: toast } = useMessage() + const appendNetworks = () => { + _appendNetworks({ + networkName: '', + portName: nanoid(), + port: 8080, + protocol: 'HTTP', + openPublicDomain: false, + publicDomain: '', + customDomain: '' + }) + } + // const networks = watch('networks') + return ( + <> + + + + {t('Network Configuration')} + + + {networks.length === 0 && appendNetworks()} />} + {networks.map((network, i) => ( + + + + {t('Container Port')} + + { + const ports = getValues('networks').map((network, index) => ({ + port: network.port, + index + })) + // 排除当前正在编辑的端口 + const isDuplicate = ports.some( + (item) => item.port === value && item.index !== i + ) + return !isDuplicate || t('The port number cannot be repeated') + } + } + })} + /> + {i === networks.length - 1 && networks.length < 5 && ( + + appendNetworks()} /> + + )} + + + + {t('Open Public Access')} + + + { + const devboxName = getValues('name') + if (!devboxName) { + toast({ + title: t('Please enter the devbox name first'), + status: 'warning' + }) + return + } + updateNetworks(i, { + ...getValues('networks')[i], + networkName: network.networkName || `${devboxName}-${nanoid()}`, + protocol: network.protocol || 'HTTP', + openPublicDomain: e.target.checked, + publicDomain: network.publicDomain || `${nanoid()}.${env.ingressDomain}` + }) + }} + /> + + + {network.openPublicDomain && ( + <> + + + + { + updateNetworks(i, { + ...getValues('networks')[i], + protocol: val + }) + }} + /> + + + {network.customDomain ? network.customDomain : network.publicDomain!} + + + setCustomAccessModalData({ + publicDomain: network.publicDomain!, + customDomain: network.customDomain! + }) + }> + {t('Custom Domain')} + + + + + + )} + {networks.length >= 1 && ( + + + } + onClick={() => removeNetworks(i)} + /> + + )} + + ))} + + + {!!customAccessModalData && ( + setCustomAccessModalData(undefined)} + onSuccess={(e) => { + const i = networks.findIndex( + (item) => item.publicDomain === customAccessModalData.publicDomain + ) + if (i === -1) return + updateNetworks(i, { + ...networks[i], + customDomain: e + }) + setCustomAccessModalData(undefined) + }} + /> + )} + + ) +} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/index.tsx new file mode 100644 index 00000000000..4743877d35e --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/index.tsx @@ -0,0 +1,174 @@ +'use client' + +import { Box, Flex, Grid, useTheme } from '@chakra-ui/react' +import { Tabs } from '@sealos/ui' +import { throttle } from 'lodash' +import { useTranslations } from 'next-intl' +import { useEffect, useState } from 'react' +import { useFormContext } from 'react-hook-form' + +import MyIcon from '@/components/Icon' +import PriceBox from '@/components/PriceBox' +import QuotaBox from '@/components/QuotaBox' +import { useRouter } from '@/i18n' + +import { useDevboxStore } from '@/stores/devbox' + +import type { DevboxEditTypeV2 } from '@/types/devbox' +import { obj2Query } from '@/utils/tools' +import BasicConfiguration from './BasicConfiguration' +import NetworkConfiguration from './NetworkConfiguration' + +const Form = ({ pxVal, isEdit }: { pxVal: number; isEdit: boolean }) => { + const theme = useTheme() + const router = useRouter() + const t = useTranslations() + const { watch } = useFormContext() + const navList: { id: string; label: string; icon: string }[] = [ + { + id: 'baseInfo', + label: t('basic_configuration'), + icon: 'formInfo' + }, + { + id: 'network', + label: t('Network Configuration'), + icon: 'network' + } + ] + const [activeNav, setActiveNav] = useState(navList[0].id) + const { devboxList } = useDevboxStore() + + // listen scroll and set activeNav + useEffect(() => { + const scrollFn = throttle((e: Event) => { + if (!e.target) return + const doms = navList.map((item) => ({ + dom: document.getElementById(item.id), + id: item.id + })) + + const dom = e.target as HTMLDivElement + const scrollTop = dom.scrollTop + for (let i = doms.length - 1; i >= 0; i--) { + const offsetTop = doms[i].dom?.offsetTop || 0 + + if (scrollTop + 500 >= offsetTop) { + setActiveNav(doms[i].id) + break + } + } + }, 200) + document.getElementById('form-container')?.addEventListener('scroll', scrollFn) + return () => { + document.getElementById('form-container')?.removeEventListener('scroll', scrollFn) + } + // eslint-disable-next-line + }, []) + + const boxStyles = { + border: theme.borders.base, + borderRadius: 'lg', + mb: 4, + bg: 'white' + } + + return ( + + {/* left sidebar */} + + + router.replace( + `/devbox/create?${obj2Query({ + type: 'yaml' + })}` + ) + } + /> + + {navList.map((item) => ( + { + setActiveNav(item.id) + window.location.hash = item.id + }}> + + + + {item.label} + + + ))} + + + + + + + + + {/* right content */} + + {/* base info */} + + {/* network */} + + + + ) +} + +export default Form diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/page.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/page.tsx index f005fa3e8c5..3aac4e86e37 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/page.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/page.tsx @@ -1,127 +1,65 @@ 'use client' import { useRouter } from '@/i18n' -import dynamic from 'next/dynamic' -import debounce from 'lodash/debounce' -import { useMessage } from '@sealos/ui' -import { customAlphabet } from 'nanoid' -import { useForm } from 'react-hook-form' import { Box, Flex } from '@chakra-ui/react' -import { useTranslations } from 'next-intl' +import { useMessage } from '@sealos/ui' import { useQuery } from '@tanstack/react-query' +import { useTranslations } from 'next-intl' +import dynamic from 'next/dynamic' import { useSearchParams } from 'next/navigation' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { FormProvider, useForm } from 'react-hook-form' -import Form from './components/Form' -import Yaml from './components/Yaml' +import Form from './components/form' import Header from './components/Header' +import Yaml from './components/Yaml' import type { YamlItemType } from '@/types' -import type { DevboxEditType, DevboxKindsType, ProtocolType } from '@/types/devbox' +import type { DevboxEditType, DevboxEditTypeV2, DevboxKindsType } from '@/types/devbox' import { useConfirm } from '@/hooks/useConfirm' import { useLoading } from '@/hooks/useLoading' +import { useDevboxStore } from '@/stores/devbox' import { useEnvStore } from '@/stores/env' +import { useGlobalStore } from '@/stores/global' import { useIDEStore } from '@/stores/ide' import { useUserStore } from '@/stores/user' -import { useDevboxStore } from '@/stores/devbox' -import { useGlobalStore } from '@/stores/global' -import { useRuntimeStore } from '@/stores/runtime' -import { patchYamlList } from '@/utils/tools' import { createDevbox, updateDevbox } from '@/api/devbox' -import { json2Devbox, json2Ingress, json2Service } from '@/utils/json2Yaml' -import { - FrameworkTypeEnum, - LanguageTypeEnum, - OSTypeEnum, - defaultDevboxEditValue, - editModeMap -} from '@/constants/devbox' +import { defaultDevboxEditValueV2, editModeMap } from '@/constants/devbox' +import { useTemplateStore } from '@/stores/template' +import { generateYamlList } from '@/utils/json2Yaml' +import { patchYamlList } from '@/utils/tools' +import { debounce } from 'lodash' const ErrorModal = dynamic(() => import('@/components/modals/ErrorModal')) - -const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 12) - const DevboxCreatePage = () => { + const { env } = useEnvStore() + const generateDefaultYamlList = () => generateYamlList(defaultDevboxEditValueV2, env) const router = useRouter() const t = useTranslations() - const searchParams = useSearchParams() const { message: toast } = useMessage() - - const { env } = useEnvStore() const { addDevboxIDE } = useIDEStore() const { checkQuotaAllow } = useUserStore() const { setDevboxDetail, devboxList } = useDevboxStore() - const { runtimeNamespaceMap, languageVersionMap, frameworkVersionMap, osVersionMap } = - useRuntimeStore() const crOldYamls = useRef([]) const formOldYamls = useRef([]) - const oldDevboxEditData = useRef() + const oldDevboxEditData = useRef() const { Loading, setIsLoading } = useLoading() const [errorMessage, setErrorMessage] = useState('') - const [forceUpdate, setForceUpdate] = useState(false) const [yamlList, setYamlList] = useState([]) const tabType = searchParams.get('type') || 'form' const devboxName = searchParams.get('name') || '' - const runtime = searchParams.get('runtime') || '' - - const formData2Yamls = (data: DevboxEditType) => [ - { - filename: 'devbox.yaml', - value: json2Devbox(data, runtimeNamespaceMap, env.devboxAffinityEnable, env.squashEnable) - }, - ...(data.networks.length > 0 - ? [ - { - filename: 'service.yaml', - value: json2Service(data) - } - ] - : []), - ...(data.networks.find((item) => item.openPublicDomain) - ? [ - { - filename: 'ingress.yaml', - value: json2Ingress(data, env.ingressSecret) - } - ] - : []) - ] - - const defaultEdit = { - ...defaultDevboxEditValue, - runtimeType: runtime || LanguageTypeEnum.go, - runtimeVersion: runtime - ? languageVersionMap[runtime as LanguageTypeEnum]?.[0]?.id || - frameworkVersionMap[runtime as FrameworkTypeEnum]?.[0]?.id || - osVersionMap[runtime as OSTypeEnum]?.[0]?.id - : languageVersionMap[LanguageTypeEnum.go]?.[0]?.id, - networks: ( - languageVersionMap[runtime as LanguageTypeEnum]?.[0]?.defaultPorts || - frameworkVersionMap[runtime as FrameworkTypeEnum]?.[0]?.defaultPorts || - osVersionMap[runtime as OSTypeEnum]?.[0]?.defaultPorts || - languageVersionMap[LanguageTypeEnum.go]?.[0]?.defaultPorts - ).map((port) => ({ - networkName: `${defaultDevboxEditValue.name}-${nanoid()}`, - portName: nanoid(), - port: port, - protocol: 'HTTP' as ProtocolType, - openPublicDomain: true, - publicDomain: `${nanoid()}.${env.ingressDomain}`, - customDomain: '' - })) - } // NOTE: need to explain why this is needed // fix a bug: searchParams will disappear when go into this page const [captureDevboxName, setCaptureDevboxName] = useState('') - + const { updateTemplateModalConfig, config: templateConfig } = useTemplateStore() useEffect(() => { const name = searchParams.get('name') if (name) { @@ -150,84 +88,42 @@ const DevboxCreatePage = () => { return val }, [screenWidth]) - const generateYamlList = (data: DevboxEditType) => { - return [ - { - filename: 'devbox.yaml', - value: json2Devbox(data, runtimeNamespaceMap, env.devboxAffinityEnable, env.squashEnable) - }, - ...(data.networks.length > 0 - ? [ - { - filename: 'service.yaml', - value: json2Service(data) - } - ] - : []), - ...(data.networks.find((item) => item.openPublicDomain) - ? [ - { - filename: 'ingress.yaml', - value: json2Ingress(data, env.ingressSecret) - } - ] - : []) - ] - } - - const formHook = useForm({ - defaultValues: defaultEdit + const formHook = useForm({ + defaultValues: defaultDevboxEditValueV2 }) - // eslint-disable-next-line react-hooks/exhaustive-deps - const formOnchangeDebounce = useCallback( - debounce((data: DevboxEditType) => { - try { - setYamlList(generateYamlList(data)) - } catch (error) { - console.log(error) - } - }, 200), + // updateyamlList every time yamlList change + const debouncedUpdateYaml = useMemo( + () => + debounce((data: DevboxEditTypeV2, env) => { + try { + const newYamlList = generateYamlList(data, env) + setYamlList(newYamlList) + } catch (error) { + console.error('Failed to generate yaml:', error) + } + }, 300), [] ) - // watch form change, compute new yaml - formHook.watch((data) => { - data && formOnchangeDebounce(data as DevboxEditType) - setForceUpdate(!forceUpdate) - }) + // 监听表单变化 + useEffect(() => { + const subscription = formHook.watch((value) => { + if (value) { + debouncedUpdateYaml(value as DevboxEditTypeV2, env) + } + }) + return () => { + subscription.unsubscribe() + debouncedUpdateYaml.cancel() + } + }, [formHook, debouncedUpdateYaml, env]) useQuery( ['initDevboxCreateData'], () => { if (!devboxName) { - setYamlList([ - { - filename: 'devbox.yaml', - value: json2Devbox( - defaultEdit, - runtimeNamespaceMap, - env.devboxAffinityEnable, - env.squashEnable - ) - }, - ...(defaultEdit.networks.length > 0 - ? [ - { - filename: 'service.yaml', - value: json2Service(defaultEdit) - } - ] - : []), - ...(defaultEdit.networks.find((item) => item.openPublicDomain) - ? [ - { - filename: 'ingress.yaml', - value: json2Ingress(defaultEdit, env.ingressSecret) - } - ] - : []) - ]) + setYamlList(generateDefaultYamlList()) return null } setIsLoading(true) @@ -239,8 +135,8 @@ const DevboxCreatePage = () => { return } oldDevboxEditData.current = res - formOldYamls.current = formData2Yamls(res) - crOldYamls.current = generateYamlList(res) as DevboxKindsType[] + formOldYamls.current = generateYamlList(res, env) + crOldYamls.current = generateYamlList(res, env) as DevboxKindsType[] formHook.reset(res) }, onError(err) { @@ -254,14 +150,12 @@ const DevboxCreatePage = () => { } } ) - - const submitSuccess = async (formData: DevboxEditType) => { + const submitSuccess = async (formData: DevboxEditTypeV2) => { setIsLoading(true) - try { // quote check const quoteCheckRes = checkQuotaAllow( - { ...formData, nodeports: devboxList.length + 1 } as DevboxEditType & { + { ...formData, nodeports: devboxList.length + 1 } as DevboxEditTypeV2 & { nodeports: number }, { @@ -280,45 +174,55 @@ const DevboxCreatePage = () => { isClosable: true }) } - const parsedNewYamlList = yamlList.map((item) => item.value) - const parsedOldYamlList = formOldYamls.current.map((item) => item.value) - - const areYamlListsEqual = - new Set(parsedNewYamlList).size === new Set(parsedOldYamlList).size && - [...new Set(parsedNewYamlList)].every((item) => new Set(parsedOldYamlList).has(item)) - if (areYamlListsEqual) { - setIsLoading(false) - return toast({ - status: 'info', - title: t('No changes detected'), - duration: 5000, - isClosable: true - }) - } - // create or update + // update if (isEdit) { + const yamlList = generateYamlList(formData, env) + setYamlList(yamlList) + const parsedNewYamlList = yamlList.map((item) => item.value) + const parsedOldYamlList = formOldYamls.current.map((item) => item.value) + const areYamlListsEqual = + new Set(parsedNewYamlList).size === new Set(parsedOldYamlList).size && + [...new Set(parsedNewYamlList)].every((item) => new Set(parsedOldYamlList).has(item)) + if (areYamlListsEqual) { + setIsLoading(false) + return toast({ + status: 'info', + title: t('No changes detected'), + duration: 5000, + isClosable: true + }) + } + if (!parsedNewYamlList) { + // prevent empty yamlList + return setErrorMessage(t('submit_form_error')) + } const patch = patchYamlList({ parsedOldYamlList: parsedOldYamlList, parsedNewYamlList: parsedNewYamlList, originalYamlList: crOldYamls.current }) - await updateDevbox({ patch, devboxName: formData.name }) } else { - await createDevbox({ devboxForm: formData, runtimeNamespaceMap }) + await createDevbox({ devboxForm: formData }) } - addDevboxIDE('cursor', formData.name) + addDevboxIDE('vscode', formData.name) toast({ title: t(applySuccess), status: 'success' }) + updateTemplateModalConfig({ + ...templateConfig, + lastRoute + }) router.push(lastRoute) } catch (error) { - console.error(error) - setErrorMessage(JSON.stringify(error)) + console.log('error', error) + if (error instanceof String && error.includes('402')) { + setErrorMessage(t('outstanding_tips')) + } else setErrorMessage(JSON.stringify(error)) } setIsLoading(false) } @@ -345,30 +249,36 @@ const DevboxCreatePage = () => { return ( <> - -
- formHook.handleSubmit((data) => openConfirm(() => submitSuccess(data))(), submitError)() - } - /> - - {tabType === 'form' ? ( -
- ) : ( - - )} -
- + + +
+ formHook.handleSubmit( + (data) => openConfirm(() => submitSuccess(data))(), + submitError + )() + } + /> + + {tabType === 'form' ? ( + + ) : ( + + )} + + + + {!!errorMessage && ( setErrorMessage('')} /> )} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/BasicInfo.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/BasicInfo.tsx index e59705e3f6d..c24a8755923 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/BasicInfo.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/BasicInfo.tsx @@ -1,15 +1,14 @@ +import { Box, Flex, Image, Spinner, Text, Tooltip } from '@chakra-ui/react' import { useMessage } from '@sealos/ui' import { useTranslations } from 'next-intl' -import React, { useCallback, useState } from 'react' -import { Box, Text, Flex, Image, Spinner, Tooltip } from '@chakra-ui/react' +import { useCallback, useState } from 'react' import MyIcon from '@/components/Icon' import { DevboxDetailType } from '@/types/devbox' -import { useEnvStore } from '@/stores/env' import { useDevboxStore } from '@/stores/devbox' -import { useRuntimeStore } from '@/stores/runtime' +import { useEnvStore } from '@/stores/env' const BasicInfo = () => { const t = useTranslations() @@ -17,12 +16,12 @@ const BasicInfo = () => { const { env } = useEnvStore() const { devboxDetail } = useDevboxStore() - const { getRuntimeDetailLabel } = useRuntimeStore() + // const { getRuntimeDetailLabel } = useRuntimeStore() const [loading, setLoading] = useState(false) - + const handleCopySSHCommand = useCallback(() => { - const sshCommand = `ssh -i yourPrivateKeyPath ${devboxDetail?.sshConfig?.sshUser}@${env.sealosDomain} -p ${devboxDetail.sshPort}` + const sshCommand = `ssh -i yourPrivateKeyPath ${devboxDetail?.sshConfig?.sshUser}@${env.sealosDomain} -p ${devboxDetail?.sshPort}` navigator.clipboard.writeText(sshCommand).then(() => { toast({ title: t('copy_success'), @@ -31,7 +30,7 @@ const BasicInfo = () => { isClosable: true }) }) - }, [devboxDetail?.sshConfig?.sshUser, devboxDetail.sshPort, env.sealosDomain, toast, t]) + }, [devboxDetail?.sshConfig?.sshUser, devboxDetail?.sshPort, env.sealosDomain, toast, t]) const handleDownloadConfig = useCallback( async (config: DevboxDetailType['sshConfig']) => { @@ -44,7 +43,7 @@ const BasicInfo = () => { const a = document.createElement('a') a.style.display = 'none' a.href = url - a.download = devboxDetail.name + a.download = devboxDetail?.name || '' document.body.appendChild(a) a.click() window.URL.revokeObjectURL(url) @@ -52,7 +51,7 @@ const BasicInfo = () => { setLoading(false) }, - [devboxDetail] + [devboxDetail?.name] ) return ( @@ -75,11 +74,11 @@ const BasicInfo = () => { ml={2} width={'20px'} height={'20px'} - alt={devboxDetail?.runtimeType} - src={`/images/${devboxDetail?.runtimeType}.svg`} onError={(e) => { e.currentTarget.src = '/images/custom.svg' }} + alt={devboxDetail?.iconId} + src={`/images/${devboxDetail?.iconId}.svg`} /> @@ -106,8 +105,15 @@ const BasicInfo = () => { {t('start_runtime')} - - {getRuntimeDetailLabel(devboxDetail?.runtimeType, devboxDetail?.runtimeVersion)} + + { + // getRuntimeDetailLabel(devboxDetail?., devboxDetail?.runtimeVersion) + `${devboxDetail?.templateRepositoryName}-${devboxDetail?.templateName}` + } @@ -124,7 +130,7 @@ const BasicInfo = () => { CPU Limit - {devboxDetail?.cpu / 1000} Core + {(devboxDetail?.cpu || 0) / 1000} Core @@ -132,7 +138,7 @@ const BasicInfo = () => { Memory Limit - {devboxDetail?.memory / 1024} G + {(devboxDetail?.memory || 0) / 1024} G @@ -174,7 +180,7 @@ const BasicInfo = () => { _hover={{ color: 'blue.500' }} onClick={handleCopySSHCommand} w={'full'}> - {`ssh -i yourPrivateKeyPath ${devboxDetail?.sshConfig?.sshUser}@${env.sealosDomain} -p ${devboxDetail.sshPort}`} + {`ssh -i yourPrivateKeyPath ${devboxDetail?.sshConfig?.sshUser}@${env.sealosDomain} -p ${devboxDetail?.sshPort}`} @@ -267,9 +273,6 @@ const BasicInfo = () => { h={'16px'} color={'grayModern.600'} mt={'1px'} - onClick={() => { - console.log('click') - }} /> diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Header.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Header.tsx index 7715a12ab2e..8e920f49e10 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Header.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Header.tsx @@ -1,19 +1,19 @@ +import { Box, Button, Flex } from '@chakra-ui/react' import { useMessage } from '@sealos/ui' import { useTranslations } from 'next-intl' -import { Flex, Button, Box } from '@chakra-ui/react' import { Dispatch, useCallback, useMemo, useState } from 'react' +import { pauseDevbox, restartDevbox, startDevbox } from '@/api/devbox' import { useRouter } from '@/i18n' import { useDevboxStore } from '@/stores/devbox' import { useGlobalStore } from '@/stores/global' -import { pauseDevbox, restartDevbox, startDevbox } from '@/api/devbox' -import { DevboxDetailType } from '@/types/devbox' +import { DevboxDetailTypeV2 } from '@/types/devbox' +import DevboxStatusTag from '@/components/DevboxStatusTag' import MyIcon from '@/components/Icon' import IDEButton from '@/components/IDEButton' import DelModal from '@/components/modals/DelModal' -import DevboxStatusTag from '@/components/DevboxStatusTag' import { sealosApp } from 'sealos-desktop-sdk/app' import { useQuery } from '@tanstack/react-query' @@ -33,7 +33,7 @@ const Header = ({ const { devboxDetail, setDevboxList } = useDevboxStore() const { screenWidth, setLoading } = useGlobalStore() - const [delDevbox, setDelDevbox] = useState(null) + const [delDevbox, setDelDevbox] = useState(null) const isBigButton = useMemo(() => screenWidth > 1000, [screenWidth]) const { refetch: refetchDevboxList } = useQuery(['devboxListQuery'], setDevboxList, { @@ -43,7 +43,7 @@ const Header = ({ }) const handlePauseDevbox = useCallback( - async (devbox: DevboxDetailType) => { + async (devbox: DevboxDetailTypeV2) => { try { setLoading(true) await pauseDevbox({ devboxName: devbox.name }) @@ -64,7 +64,7 @@ const Header = ({ [refetchDevboxDetail, setLoading, t, toast] ) const handleRestartDevbox = useCallback( - async (devbox: DevboxDetailType) => { + async (devbox: DevboxDetailTypeV2) => { try { setLoading(true) await restartDevbox({ devboxName: devbox.name }) @@ -85,7 +85,7 @@ const Header = ({ [setLoading, t, toast, refetchDevboxDetail] ) const handleStartDevbox = useCallback( - async (devbox: DevboxDetailType) => { + async (devbox: DevboxDetailTypeV2) => { try { setLoading(true) await startDevbox({ devboxName: devbox.name }) @@ -106,7 +106,7 @@ const Header = ({ [setLoading, t, toast, refetchDevboxDetail] ) const handleGoToTerminal = useCallback( - async (devbox: DevboxDetailType) => { + async (devbox: DevboxDetailTypeV2) => { const defaultCommand = `kubectl exec -it $(kubectl get po -l app.kubernetes.io/name=${devbox.name} -oname) -- sh -c "clear; (bash || ash || sh)"` try { sealosApp.runEvents('openDesktopApp', { @@ -126,6 +126,7 @@ const Header = ({ }, [t, toast] ) + if (!devboxDetail) return null return ( {/* left back button and title */} @@ -168,7 +169,6 @@ const Header = ({ {devboxDetail.status.value === 'Running' && ( - {t('memory')}{' '} + {t('memory')} {devboxDetail?.usedMemory?.yData[devboxDetail?.usedMemory?.yData?.length - 1]}% @@ -154,7 +155,7 @@ const MainBody = () => { {t('network')} ( {devboxDetail?.networks?.length} ) - {devboxDetail?.networks?.length > 0 ? ( + {devboxDetail?.networks && devboxDetail.networks.length > 0 ? ( { + const { startReleaseGuide } = useReleaseDriver() const t = useTranslations() const { message: toast } = useMessage() const { Loading, setIsLoading } = useLoading() @@ -38,31 +49,60 @@ const Version = () => { const [apps, setApps] = useState([]) const [deployData, setDeployData] = useState(null) const [currentVersion, setCurrentVersion] = useState(null) - + const [updateTemplateRepo, setUpdateTemplateRepo] = useState< + | null + | Awaited>['templateRepositoryList'][number] + >(null) + const createTemplateModalHandler = useDisclosure() + const selectTemplalteModalHandler = useDisclosure() + const updateTemplateModalHandler = useDisclosure() const { openConfirm, ConfirmChild } = useConfirm({ content: 'delete_version_confirm_info' }) - const { refetch } = useQuery( ['initDevboxVersionList'], - () => setDevboxVersionList(devbox.name, devbox.id), + () => setDevboxVersionList(devbox!.name, devbox!.id), { refetchInterval: devboxVersionList.length > 0 && + !createTemplateModalHandler.isOpen && + !updateTemplateModalHandler.isOpen && + !selectTemplalteModalHandler.isOpen && devboxVersionList[0].status.value === DevboxReleaseStatusEnum.Pending ? 3000 : false, onSettled() { setInitialized(true) - } + }, + enabled: !!devbox } ) + useEffect(() => { + if (devboxVersionList?.length && devboxVersionList.length > 0) { + startReleaseGuide() + } + }, [devboxVersionList.length]) + + const listPrivateTemplateRepositoryQuery = useQuery( + ['template-repository-list', 'template-repository-private'], + () => { + return listPrivateTemplateRepository({ + page: 1, + pageSize: 100 + }) + } + ) + const templateRepositoryList = + listPrivateTemplateRepositoryQuery.data?.templateRepositoryList || [] const handleDeploy = useCallback( async (version: DevboxVersionListItemType) => { - const devboxId = devbox.id - - const { releaseCommand, releaseArgs } = await getSSHRuntimeInfo(devbox.runtimeVersion) + // const { releaseCommand, releaseArgs } = await getSSHRuntimeInfo(devbox.runtimeVersion) + if (!devbox) return + const result = await getTemplateConfig(devbox.templateUid) + const config = parseTemplateConfig(result.template.config) + const releaseArgs = config.releaseArgs.join(' ') + const releaseCommand = config.releaseCommand.join(' ') const { cpu, memory, networks, name } = devbox const newNetworks = networks.map((network) => { return { @@ -75,7 +115,7 @@ const Version = () => { const imageName = `${env.registryAddr}/${env.namespace}/${devbox.name}:${version.tag}` const transformData = { - appName: `${name}-release`, + appName: `${name}-release-${nanoid()}`, cpu: cpu, memory: memory, imageName: imageName, @@ -93,11 +133,11 @@ const Version = () => { runCMD: releaseCommand, cmdParam: releaseArgs, labels: { - [devboxIdKey]: devboxId + [devboxIdKey]: devbox.id } } setDeployData(transformData) - const apps = await getAppsByDevboxId(devboxId) + const apps = await getAppsByDevboxId(devbox.id) // when: there is no app,create a new app if (apps.length === 0) { @@ -121,7 +161,6 @@ const Version = () => { }, [devbox, env.ingressDomain, env.namespace, env.registryAddr] ) - const handleDelDevboxVersion = useCallback( async (versionName: string) => { try { @@ -189,13 +228,8 @@ const Version = () => { title: t('version_description'), key: 'description', render: (item: DevboxVersionListItemType) => ( - - + + {item.description} @@ -205,12 +239,14 @@ const Version = () => { title: t('control'), key: 'control', render: (item: DevboxVersionListItemType) => ( - + + + + + + + ) +} +export default OverviewTemplateVersionModal \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/PublicPanel.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/PublicPanel.tsx new file mode 100644 index 00000000000..dd92a6e73e2 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/PublicPanel.tsx @@ -0,0 +1,249 @@ +import { listTag, listTemplateRepository } from "@/api/template"; +import SwitchPage from "@/components/SwitchPage"; +import { Tag, TagType } from "@/prisma/generated/client"; +import { createTagSelectorStore } from "@/stores/tagSelector"; +import { getLangStore } from "@/utils/cookie"; +import { Box, Divider, Flex, FlexProps, Grid, Heading, TabPanel, Text } from "@chakra-ui/react"; +import { useQuery } from "@tanstack/react-query"; +import { useTranslations } from "next-intl"; +import { createContext, useContext, useEffect, useState } from "react"; +import { useStore } from "zustand"; +import { TagCheckbox } from "../TagCheckbox"; +import TemplateCard from "./TemplateCard"; +const TagSelectorStoreCtx = createContext | null>(null) +const TagItem = ({ tag, ...props }: { tag: Tag } & FlexProps) => { + const store = useContext(TagSelectorStoreCtx) + if (!store) throw new Error('TagSelectorStoreCtx is null') + const { selectedTagList, setSelectedTag } = useStore(store,) + const lastLang = getLangStore() + return !tag ? null : + { + const selected = e.target.checked + setSelectedTag(tag.uid, selected) + }} + > + + { + tag[lastLang === 'zh' ? 'zhName' : 'enName'] || tag.name + } + + +} +const TagList = ({ tags, title }: { tags: Tag[], title: string }) => { + const t = useTranslations() + return + {title} + { + tags.map((tag, index) => ( + + ))} + + +} +const PublicPanel = ({ + search, }: { + search: string, + }) => { + const [tagSelectorStore] = useState(createTagSelectorStore()) + return + <_PublicPanel + search={search} + /> + +} +function _PublicPanel({ + search, }: { + search: string, + }) { + const tagsQuery = useQuery( + ['template-repository-tags'], + listTag, + { + staleTime: Infinity, + cacheTime: Infinity, + } + ) + let tags = (tagsQuery.data?.tagList || []).sort((a, b) => a.name === 'official' ? -1 : b.name === 'official' ? 1 : 0) + let tagListCollection = tags.reduce((acc, tag) => { + if (!acc[tag.type]) { + acc[tag.type] = []; + } + acc[tag.type].push(tag); + return acc; + }, { + [TagType.OFFICIAL_CONTENT]: [], + [TagType.USE_CASE]: [], + [TagType.PROGRAMMING_LANGUAGE]: [] + } as Record + ); + const store = useContext(TagSelectorStoreCtx) + if (!store) throw new Error('TagSelectorStoreCtx is null') + const state = useStore(store) + + + const [pageQueryBody, setPageQueryBody] = useState({ + page: 1, + pageSize: 30, + totalItems: 0, + totalPage: 0, + }) + + // reset query + useEffect(() => { + if (!search) return + setPageQueryBody(prev => ({ + ...prev, + page: 1, + totalItems: 0, + totalPage: 0, + })) + }, [search]) + // reset query + useEffect(() => { + setPageQueryBody(prev => ({ + ...prev, + page: 1, + totalItems: 0, + totalPage: 0, + })) + }, [state.selectedTagList]) + const queryBody = { + page: pageQueryBody.page, + pageSize: pageQueryBody.pageSize, + search, + tags: state.getSelectedTagList(), + } + const listTemplateReposistory = useQuery( + ['template-repository-list', 'template-repository-public', queryBody], + () => { + return listTemplateRepository({ + page: queryBody.page, + pageSize: queryBody.pageSize, + }, queryBody.tags, queryBody.search) + }, + ) + + useEffect(() => { + if (listTemplateReposistory.isSuccess && listTemplateReposistory.data) { + const data = listTemplateReposistory.data.page + setPageQueryBody(prev => ({ + ...prev, + totalItems: data.totalItems || 0, + totalPage: data.totalPage || 0, + page: data.page || 1 + })) + } + }, [listTemplateReposistory.data]) // 添加依赖项 + + const tempalteReposistoryList = listTemplateReposistory.data?.templateRepositoryList || [] + const t = useTranslations() + return + + {/* Left Sidebar */} + + + {t('tags')} + + + + + {/* */} + + + + + + + + + + + + {/* Right Content */} + + + {t('all_templates')} + + + + {tempalteReposistoryList.map((tr) => { + return t.tag)} + isPublic + /> + })} + + + + { + setPageQueryBody(page => { + return { + ...page, + page: currentPage, + } + }) + }} + /> + + + + ; +} +export default PublicPanel \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/TemplateCard.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/TemplateCard.tsx new file mode 100644 index 00000000000..176e505ba03 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/TemplateCard.tsx @@ -0,0 +1,263 @@ +import MyIcon from '@/components/Icon'; +import { useRouter } from '@/i18n'; +import { type Tag as TTag } from '@/prisma/generated/client'; +import { useDevboxStore } from '@/stores/devbox'; +import { useTemplateStore } from '@/stores/template'; +import { Box, BoxProps, Button, Flex, Img, MenuButton, Tag, Text, useDisclosure } from '@chakra-ui/react'; +import { MyTooltip, SealosMenu } from '@sealos/ui'; +import { useLocale, useTranslations } from 'next-intl'; +import DeleteTemplateReposistoryModal from '../updateTemplate/DeleteTemplateReposistoryModal'; +import EditTemplateModal from '../updateTemplate/EditTemplateModal'; +import EditTemplateRepositoryModal from '../updateTemplate/EditTemplateReposistoryModal'; +const TemplateCard = ({ isPublic, iconId, templateRepositoryName, templateRepositoryDescription + , templateRepositoryUid, + isDisabled = false, + inPublicStore = true, + tags, + ...props +}: { + isPublic?: boolean + iconId: string, + isDisabled?: boolean, + inPublicStore?: boolean, + templateRepositoryName: string, + templateRepositoryDescription: string | null + templateRepositoryUid: string, + tags: TTag[] +} & BoxProps) => { + const t = useTranslations() + const { closeTemplateModal, config, updateTemplateModalConfig } = useTemplateStore() + const editTemplateHandle = useDisclosure() + const editTemplateRepositoryHandle = useDisclosure() + const deleteTemplateHandle = useDisclosure() + const { setStartedTemplate } = useDevboxStore() + const router = useRouter() + const description = templateRepositoryDescription ? templateRepositoryDescription : t('no_description') + const lastLang = useLocale() + return ( + <> + + + + + {/* Python Logo */} + + + {/* Title and Description */} + + + + {templateRepositoryName} + + {inPublicStore ? + (tags.findIndex(tag => tag.name === 'official') !== -1 ? + + + + : <>) + : + (isPublic ? + + {t('public')} + : + + {t('private')} + ) + } + + + + + {description} + + + + + + + + {/* Buttons */} + + + {!inPublicStore && + + + + } + menuList={[ + { + child: ( + <> + + {t('edit')} + + ), + onClick: editTemplateRepositoryHandle.onOpen + }, + { + child: ( + <> + + {t('version_manage')} + + ), + onClick: editTemplateHandle.onOpen + }, + { + child: ( + <> + + {t('delete')} + + ), + onClick: deleteTemplateHandle.onOpen + }, + ]} + /> + } + + + + {/* Tags */} + + {tags.filter(tag => tag.name !== 'official').map(tag => + {tag[lastLang === 'zh' ? 'zhName' : 'enName'] || tag.name} + )} + + + + + + + ); +}; + +export default TemplateCard; \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/index.tsx new file mode 100644 index 00000000000..f015206102e --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/TemplateModal/index.tsx @@ -0,0 +1,165 @@ +"use client" + +import MyIcon from '@/components/Icon' +import { TemplateState } from '@/constants/template' +import { usePathname } from '@/i18n' +import { useTemplateStore } from '@/stores/template' +import { + calc, + Input, + InputGroup, + InputLeftElement, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalOverlay, + Tab, + TabList, + TabPanels, + Tabs, + Text +} from '@chakra-ui/react' +import { SearchIcon } from '@sealos/ui' +import { debounce } from 'lodash' +import { useTranslations } from 'next-intl' +import { useCallback, useState } from 'react' +import PrivatePanel from './PrivatePanel' +import PublicPanel from './PublicPanel' + + +const TemplateModal = () => { + const t = useTranslations() + const { isOpen, config, closeTemplateModal, openTemplateModal, updateTemplateModalConfig } = useTemplateStore() + const [search, setsearch] = useState('') + const updateSearchVal = useCallback( + debounce((val: string) => { + setsearch(val) + }, 500), + [] + ) + const lastRoute = usePathname() + return ( + { + closeTemplateModal() + updateTemplateModalConfig({ + templateState: TemplateState.publicTemplate, + lastRoute + }) + }} lockFocusAcrossFrames={false}> + + + {t('devbox_template')} + + + {/* */} + { + if (idx === 0) openTemplateModal({ + templateState: TemplateState.publicTemplate, + lastRoute + }) + else openTemplateModal({ + templateState: TemplateState.privateTemplate, + lastRoute + }) + }}> + {/* TabList must be direct child of Tabs */} + + + + + {t('all_templates')} + + + + + + {t('my_templates')} + + + + + + + { + updateSearchVal(e.target.value) + }} + /> + + + + {/* TabPanels must be direct child of Tabs */} + + + + + + {/* */} + + + + ) +} + +export default TemplateModal \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/CreateTemplateModal.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/CreateTemplateModal.tsx new file mode 100644 index 00000000000..9ed10002b9f --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/CreateTemplateModal.tsx @@ -0,0 +1,236 @@ +import { createTemplateReposistory } from '@/api/template'; +import MyFormLabel from '@/components/MyFormControl'; +import { TemplateState } from '@/constants/template'; +import { usePathname } from '@/i18n'; +import { useTemplateStore } from '@/stores/template'; +import { templateNameSchema, versionSchema } from '@/utils/vaildate'; +import { + Button, + ButtonGroup, + Flex, + Input, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Text, + VStack +} from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useTranslations } from 'next-intl'; +import { FC } from 'react'; +import { Controller, FormProvider, SubmitHandler, useForm } from 'react-hook-form'; +import { z } from 'zod'; +import TemplateRepositoryDescriptionField from './components/TemplateRepositoryDescriptionField'; +import TemplateRepositoryIsPublicField from './components/TemplateRepositoryIsPublicField'; +import TemplateRepositoryNameField from './components/TemplateRepositoryNameField'; +import TemplateRepositoryTagField from './components/TemplateRepositoryTagField'; +const tagSchema = z.object({ + value: z.string(), +}); + +// 定义表单数据类型和验证规则 + +interface CreateTemplateModalProps { + isOpen: boolean; + onClose: () => void; + devboxReleaseName: string; + // onSubmit?: (data: FormData) => void; +} +const CreateTemplateModal: FC = ({ + isOpen, + onClose, + // onSubmit + devboxReleaseName +}) => { + const t = useTranslations() + const formSchema = z.object({ + name: z.string().min(1, t('input_template_name_placeholder')).pipe(templateNameSchema), + version: z.string().min(1, t('input_template_version_placeholder')).pipe(versionSchema), + isPublic: z.boolean().default(false), + agreeTerms: z.boolean().refine((val) => val === true, t('privacy_and_security_agreement_tips')), + tags: z.array(tagSchema).min(1, t('select_at_least_1_tag')).max(3, t('select_lest_than_3_tags')), + description: z.string(), + }); + + type FormData = z.infer; + type Tag = z.infer; + const methods = useForm({ + defaultValues: { + name: '', + version: '', + isPublic: false, + agreeTerms: false, + tags: [], + description: '', + }, + mode: 'onSubmit' + }); + const { + control, + handleSubmit, + formState: { errors, isSubmitting }, + reset, + } = methods + const { openTemplateModal, config } = useTemplateStore() + const queryClient = useQueryClient() + const mutation = useMutation({ + mutationFn: createTemplateReposistory + // return await createTemplate(data) + }) + const { message: toast } = useMessage() + const lastRoute = usePathname() + const onSubmitHandler: SubmitHandler = async (_data) => { + try { + const result = formSchema.safeParse(_data) + if (!result.success) { + // const title = result.error.errors[0] + const error = result.error.errors[0] + if(error.path[0] === 'name' && error.code === 'invalid_string') { + toast({ + title: t('invalide_template_name'), + status: 'error', + }); + return; + } + if(error.path[0] === 'version' && error.code === 'invalid_string') { + toast({ + title: t('invalide_template_version'), + status: 'error', + }); + return; + } + const title = error.message + toast({ + title, + status: 'error', + }); + return; + } + const data = result.data + await mutation.mutateAsync({ + templateRepositoryName: data.name, + version: data.version, + isPublic: data.isPublic, + description: data.description, + tagUidList: data.tags.map((tag) => tag.value), + devboxReleaseName + }); + queryClient.invalidateQueries(['template-repository-list']) + queryClient.invalidateQueries(['template-repository-detail']) + reset(); + onClose(); + openTemplateModal({ + templateState: TemplateState.privateTemplate, + lastRoute + }) + toast({ + title: t('create_template_success'), + status: 'success', + }); + } catch (error) { + + if(error == '409:templateRepository name already exists') { + return toast({ + title: t('template_repository_name_already_exists'), + status: 'error', + }); + } + toast({ + title: error as string, + status: 'error', + }); + } + }; + + return ( + reset()} + > + + + + + + {t('create_template')} + + + + + {/* 名称 */} + + + {/* 版本号 */} + + + {t('version')} + ( + + )} + /> + + + + {/* 公开 */} + {/* */} + + + {/* 标签 */} + + + + {/* 简介 */} + + + + + + {/* 按钮组 */} + + + + + + + + + + ); +}; + +export default CreateTemplateModal \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/DeleteTemplateReposistoryModal.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/DeleteTemplateReposistoryModal.tsx new file mode 100644 index 00000000000..ff8fc9c6ab4 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/DeleteTemplateReposistoryModal.tsx @@ -0,0 +1,76 @@ +import { deleteTemplateRepository } from '@/api/template' +import MyIcon from '@/components/Icon' +import { + Button, + ButtonGroup, + Flex, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + ModalProps, + Text +} from '@chakra-ui/react' +import { useMutation, useQueryClient } from '@tanstack/react-query' +import { useTranslations } from 'next-intl' + +const DeleteTemplateReposistoryModal = ({ uid, templateRepositoryName, ...props }: Omit & { + templateRepositoryName: string + uid: string +}) => { + const t = useTranslations() + const mutation = useMutation({ + mutationFn: (uid: string) => { + return deleteTemplateRepository(uid) + }, + onSuccess: () => { + queryClient.invalidateQueries(['template-repository-list']) + queryClient.invalidateQueries(['template-repository-detail']) + } + }) + const queryClient = useQueryClient() + return ( + + + + + + + {t('prompt')} + + + + + {t.rich('delete_template_prompt', { + name: {templateRepositoryName} as any + })} + + + + + + + + + + ) +} + +export default DeleteTemplateReposistoryModal \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/EditTemplateModal.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/EditTemplateModal.tsx new file mode 100644 index 00000000000..3bec744dcd0 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/EditTemplateModal.tsx @@ -0,0 +1,182 @@ +import { listTemplate } from '@/api/template'; +import MyIcon from '@/components/Icon'; +import MyTable from '@/components/MyTable'; +import { + Box, + Flex, + IconButton, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalOverlay, + Text, + useDisclosure +} from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; +import dayjs from 'dayjs'; +import { useTranslations } from 'next-intl'; +import { FC, useState } from 'react'; +import { z } from 'zod'; +import DeleteTemplateVersionModal from '../updateTemplateVersion/DeleteTemplateVersionModal'; +const tagSchema = z.object({ + value: z.string().min(1), +}); +const versionSchema = z.object({ + name: z.string(), + uid: z.string(), +}); +type VersionType = z.infer; +interface CreateTemplateModalProps { + isOpen: boolean; + onClose: () => void; + onSubmit?: (data: FormData) => void; + uid: string; + templateRepositoryName: string; +} + +const EditTemplateModal: FC = ({ + isOpen, + onClose, + uid, + templateRepositoryName +}) => { + const t = useTranslations() + const DeleteTemplateVersionHandle = useDisclosure() + const [deletedTemplateVersion, setDeletedTemplateVersion] = useState() + const templateRepositoryQuery = useQuery( + ['templateList', uid], + () => listTemplate(uid), + { + enabled: isOpen + } + ) + const columns: { + title: string + dataIndex?: keyof { + uid: string; + name: string; + config: string; + image: string; + createAt: Date; + updateAt: Date; + } + minWidth?: string + key: string + render?: (item: { + uid: string; + name: string; + config: string; + image: string; + createdAt: Date; + updatedAt: Date; + }) => JSX.Element + }[] = [ + { + title: t('version'), + key: 'name', + render: (item) => { + return ( + + {item.name} + + ) + } + }, + { + title: t('creation_time'), + dataIndex: 'createAt', + key: 'createAt', + render: (item) => { + return {dayjs(item.createdAt).format('YYYY-MM-DD HH:mm')} + } + }, { + title: t('update_time'), + dataIndex: 'updateAt', + key: 'updateAt', + render: (item) => { + return {dayjs(item.updatedAt).format('YYYY-MM-DD HH:mm')} + } + }, + { + title: t('control'), + key: 'control', + minWidth: 'unset', + render: (item) => ( + // + } + minW={'unset'} + onClick={() => { + setDeletedTemplateVersion({ + name: item.name, + uid: item.uid, + }) + DeleteTemplateVersionHandle.onOpen() + }} + /> + // + ) + } + ] + const templateList = + templateRepositoryQuery.data?.templateList || [] + return (<> + + + + + {t('version_manage')} + + + + + + + + {templateList.length === 0 && + + + {t('no_template_versions')} + + } + + + + + + {!!deletedTemplateVersion && } + + ); +}; + +export default EditTemplateModal \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/EditTemplateReposistoryModal.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/EditTemplateReposistoryModal.tsx new file mode 100644 index 00000000000..dcef3b9da3e --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/EditTemplateReposistoryModal.tsx @@ -0,0 +1,210 @@ +import { getTemplateRepository, updateTemplateReposistory } from '@/api/template'; +import { TemplateVersionState } from '@/constants/template'; +import { + Button, + ButtonGroup, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Text, + useDisclosure, + VStack +} from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useTranslations } from 'next-intl'; +import { FC, useEffect, useState } from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; +import { z } from 'zod'; +import TemplateRepositoryDescriptionField from './components/TemplateRepositoryDescriptionField'; +import TemplateRepositoryIsPublicField from './components/TemplateRepositoryIsPublicField'; +import TemplateRepositoryNameField from './components/TemplateRepositoryNameField'; +import TemplateRepositoryTagField from './components/TemplateRepositoryTagField'; +const tagSchema = z.object({ + value: z.string().min(1), +}); +const versionSchema = z.object({ + name: z.string(), + uid: z.string(), + state: z.nativeEnum(TemplateVersionState) +}); +type VersionType = z.infer; +interface CreateTemplateModalProps { + isOpen: boolean; + onClose: () => void; + onSubmit?: (data: FormData) => void; + uid: string; +} + +const EditTemplateRepositoryModal: FC = ({ + isOpen, + onClose, + uid +}) => { + const t = useTranslations() + const formSchema = z.object({ + name: z.string().min(1, t('input_template_name_placeholder')), + isPublic: z.boolean().default(false), + agreeTerms: z.boolean().refine((val) => val === true, t('privacy_and_security_agreement_tips')), + tags: z.array(tagSchema).min(1, t('select_at_least_1_tag')).max(3, t('select_lest_than_3_tags')), + description: z.string(), + }); + type FormData = z.infer; + const methods = useForm({ + defaultValues: { + name: '', + isPublic: false, + agreeTerms: false, + tags: [], + description: '', + }, + }); + const { + control, + handleSubmit, + formState: { errors, isSubmitting }, + reset, + setValue, + } = methods + + const DeleteTemplateVersionHandle = useDisclosure() + const [deletedTemplateVersion, setDeletedTemplateVersion] = useState() + const templateRepositoryQuery = useQuery( + ['template-repository-detail', uid], + () => getTemplateRepository(uid), + { + enabled: isOpen + } + ) + const updateMutation = useMutation( + updateTemplateReposistory, + { + onSuccess() { + queryClient.invalidateQueries(['template-repository-list']) + queryClient.invalidateQueries(['template-repository-detail']) + } + } + ) + const templateRepository = templateRepositoryQuery.data?.templateRepository + const [publicIsDisabled, setPublicIsDisabled] = useState(false) + useEffect(() => { + if (isOpen && templateRepository && templateRepositoryQuery.isSuccess) { + setValue('tags', templateRepository.templateRepositoryTags.map(({ tag }) => ({ + value: tag.uid, + }))) + + setValue('name', templateRepository.name) + setValue('description', templateRepository.description || '') + setValue('isPublic', templateRepository.isPublic) + if (templateRepository.isPublic) { + setPublicIsDisabled(true) + setValue('agreeTerms', true) + } + } + }, [templateRepository, isOpen]) + const { message: toast } = useMessage() + const queryClient = useQueryClient() + const onSubmitHandler = async (_data: FormData) => { + try { + const result = formSchema.safeParse(_data) + if (!result.success) { + const error = result.error.errors[0] + toast({ + title: error.message, + status: 'error', + }); + return; + } + const data = result.data + await updateMutation.mutateAsync({ + uid, + templateRepositoryName: data.name, + isPublic: data.isPublic, + description: data.description, + tagUidList: data.tags.map(({ value }) => value) + }); + queryClient.invalidateQueries(['template-repository-list']) + reset(); + onClose(); + toast({ + title: t('template_ssaved_successfully'), + status: 'success', + }); + } catch (error) { + toast({ + title: error as string, + status: 'error', + }); + } + }; + return (<> + reset()} + > + + + +
+ + {t('edit_template')} + + + + + + {/* 名称 */} + + + {/* 公开 */} + + + {/* 标签 */} + + + {/* 简介 */} + + + + + + {/* 按钮组 */} + + + + + + +
+ +
+
+ + ); +}; + +export default EditTemplateRepositoryModal \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/SelectActionModal/TemplateDropdown.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/SelectActionModal/TemplateDropdown.tsx new file mode 100644 index 00000000000..34c6518c38d --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/SelectActionModal/TemplateDropdown.tsx @@ -0,0 +1,151 @@ +import MyIcon from "@/components/Icon"; +import { ChevronDownIcon } from "@chakra-ui/icons"; +import { Box, Button, ButtonProps, HStack, Img, Popover, PopoverBody, PopoverContent, PopoverTrigger, Text, useDisclosure, VStack } from "@chakra-ui/react"; + +const TemplateButton = ({ isActive = false, icon, title, description, onClick, isInMenu = false, ...props }: ButtonProps & { + icon: React.ReactNode; + title: string; + isActive?: boolean; + isInMenu?: boolean + description: string +}) => { + return ( + + ); +}; +type TRepositoryItem = { + iconId: string | null, + name: string, + description: string | null, + uid: string +} +export default function TemplateDropdown({ + templateRepositoryList, + selectedTemplateRepoUid, + setSelectedTemplateRepoUid +}: { + templateRepositoryList: TRepositoryItem[], + selectedTemplateRepoUid: string | null, + setSelectedTemplateRepoUid: (uid: string) => void +}) { + const selectedTemplateRepository = templateRepositoryList.find(t => t.uid === selectedTemplateRepoUid); + const { + isOpen, onOpen, onClose } = useDisclosure() + return ( + isOpen && onClose()} onOpen={() => !isOpen && onOpen()}> + + + } + title={selectedTemplateRepository?.name || ''} + description={selectedTemplateRepository?.description || ''} + /> + + + {/* */} + + + + {templateRepositoryList.map(({ uid, iconId, description, name }) => ( + } + isInMenu + title={name} + description={description || ''} + onClick={() => setSelectedTemplateRepoUid(uid)} + /> + ))} + + + + + + ); +}; \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/SelectActionModal/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/SelectActionModal/index.tsx new file mode 100644 index 00000000000..de780917b1b --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/SelectActionModal/index.tsx @@ -0,0 +1,83 @@ +import { + Button, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Text +} from '@chakra-ui/react'; +import { useTranslations } from 'next-intl'; + +import MyIcon from '@/components/Icon'; +import { useMessage } from '@sealos/ui'; +import { useState } from "react"; +import TemplateDropdown from './TemplateDropdown'; + + +const SelectTemplateModal = ({ + isOpen, + onClose, + onOpenCreate, + onOpenUdate, + templateRepositoryList +}: { + isOpen: boolean; + onClose: () => void; + templateRepositoryList: { iconId: string | null, name: string, description: null | string, uid: string }[] + onSubmit?: (data: FormData) => void; + onOpenCreate: () => void; + onOpenUdate: (templateRepoUid: string) => void; +}) => { + const { message } = useMessage() + const t = useTranslations() + const [selectedTemplateRepoUid, setSelectedTemplateRepoUid] = useState(templateRepositoryList?.[0]?.uid); + return ( + + + + + {t('create_or_update_template')} + + + + {t("select_template_tips")} + + + + + + + + + ); +}; + +export default SelectTemplateModal \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/UpdateTemplateRepositoryModal.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/UpdateTemplateRepositoryModal.tsx new file mode 100644 index 00000000000..5bb60056ea6 --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/UpdateTemplateRepositoryModal.tsx @@ -0,0 +1,347 @@ +import { updateTemplate } from '@/api/template'; +import MyIcon from '@/components/Icon'; +import MyFormLabel from '@/components/MyFormControl'; +import { versionSchema } from '@/utils/vaildate'; +import { + Box, + Button, + ButtonGroup, + Flex, + HStack, + Input, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Popover, + PopoverBody, + PopoverContent, + PopoverTrigger, + Text, + useDisclosure, + VStack +} from '@chakra-ui/react'; +import { AddIcon, useMessage } from '@sealos/ui'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useTranslations } from 'next-intl'; +import { FC, useEffect, useState } from 'react'; +import { FormProvider, useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import OverviewTemplateVersionModal from '../updateTemplateVersion/OverviewTemplateVersionModal'; +import TemplateRepositoryDescriptionField from './components/TemplateRepositoryDescriptionField'; +import TemplateRepositoryNameField from './components/TemplateRepositoryNameField'; +import TemplateRepositoryTagField from './components/TemplateRepositoryTagField'; + + +interface CreateTemplateModalProps { + isOpen: boolean; + onClose: () => void; + devboxReleaseName: string + templateRepository: { + uid: string; + name: string; + description: string | null; + iconId: string | null; + isPublic: boolean; + templates: { + uid: string; + name: string; + }[]; + templateRepositoryTags: { + tag: { + uid: string; + name: string; + }; + }[]; + }; +} + + +const VersionSelect = ({ templateList }: { templateList: { uid: string, name: string }[] }) => { + const { + watch, + setValue, + } = useFormContext(); + const [inputValue, setInputValue] = useState("") + + const handleVersionSelect = (version: string) => { + setInputValue(version) + setValue('version', inputValue) + handler.onClose() + } + const handler = useDisclosure() + + const handleCreateVersion = () => { + // 处理创建新版本的逻辑 + setValue('version', inputValue) + handler.onClose() + } + const t = useTranslations() + return (<> + + + + {watch('version')} + + + + + + { + setInputValue(e.target.value) + }} + // border="1px solid #219BF4" + // boxShadow="0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)" + borderRadius="4px" + fontSize="12px" + placeholder={t('search_or_add_version')} + _focus={{ + border: "1px solid #219BF4", + boxShadow: "0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)", + }} + /> + + {/* 已有版本列表 */} + {templateList + .filter(v => v.name.toLowerCase().includes(inputValue.toLowerCase())) + .map((v) => ( + handleVersionSelect(v.name)} + > + {v.name} + + ))} + + {/* 创建新版本选项 */} + {inputValue && !templateList.find(v => v.name === inputValue) && ( + + + + {t('create_template_version', { + version: inputValue, + })} + + + )} + + + + + + ) +} +const UpdateTemplateRepositoryModal: FC = ({ + isOpen, + onClose, + templateRepository, + devboxReleaseName +}) => { + const t = useTranslations() + const tagSchema = z.object({ + value: z.string(), + }); + const formSchema = z.object({ + name: z.string().min(1, t('input_template_name_placeholder')), + version: z.string().min(1, t('input_template_version_placeholder')).pipe(versionSchema), + tags: z.array(tagSchema).min(1, t('select_at_least_1_tag')).max(3, t('select_lest_than_3_tags')), + description: z.string(), + }); + const queryClient = useQueryClient() + type FormData = z.infer; + const mutation = useMutation(updateTemplate, { + onSuccess() { + queryClient.invalidateQueries(['template-repository-list']) + queryClient.invalidateQueries(['template-repository-detail']) + } + }) + const methods = useForm({ + defaultValues: { + name: '', + version: '', + // agreeTerms: false, + tags: [], + description: '', + }, + }); + const { + handleSubmit, + formState: { errors, isSubmitting }, + reset, + setValue, + watch + } = methods + useEffect(() => { + if (templateRepository && isOpen) { + setValue('tags', templateRepository.templateRepositoryTags.map(({ tag }) => ({ + value: tag.uid, + }))) + setValue('version', templateRepository.templates[0]?.name || '') + setValue('name', templateRepository.name) + setValue('description', templateRepository.description || '') + } + }, [templateRepository, isOpen]) + const { message: toast } = useMessage() + const overviewHandler = useDisclosure() + const submit = async (_data: FormData) => { + try { + const result = formSchema.safeParse(_data) + if (!result.success) { + const error = result.error.errors[0] + if(error.path[0] === 'version' && error.code === 'invalid_string') { + toast({ + title: t('invalide_template_version'), + status: 'error', + }); + return; + } + toast({ + title: error.message, + status: 'error', + }); + return; + } + const data = result.data + await mutation.mutateAsync({ + templateRepositoryUid: templateRepository?.uid || '', + version: data.version, + devboxReleaseName, + description: data.description, + tagUidList: data.tags.map(({ value }) => value), + }) + + queryClient.invalidateQueries(['template-repository-list']) + reset(); + onClose(); + toast({ + title: t('update_template_success'), + status: 'success', + }); + } catch (error) { + toast({ + title: error as string, + status: 'error', + }); + } + }; + const onSubmitHandler = (data: FormData) => { + if (templateRepository.templates.findIndex(d => data.version === d.name) > -1) { + overviewHandler.onOpen() + return + } + return submit(data) + }; + + return (<> + reset()} + > + + + +
+ + {t('update_template')} + + + + + {/* 名称 */} + + + {/* 版本号 */} + + {t('version')} + + + + + + {/* 标签 */} + + + {/* 简介 */} + + + + + + + {/* 按钮组 */} + + + + + + +
+ +
+
+ { + submit(methods.getValues()) + }} version={watch('version')} template={templateRepository.name} /> + + ); +}; + +export default UpdateTemplateRepositoryModal \ No newline at end of file diff --git a/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/components/TemplateRepositoryDescriptionField.tsx b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/components/TemplateRepositoryDescriptionField.tsx new file mode 100644 index 00000000000..a9f978b66af --- /dev/null +++ b/frontend/providers/devbox/app/[lang]/(platform)/template/updateTemplate/components/TemplateRepositoryDescriptionField.tsx @@ -0,0 +1,28 @@ +import MyFormLabel from "@/components/MyFormControl"; +import { Flex, Textarea } from "@chakra-ui/react"; +import { useTranslations } from "next-intl"; +import { Controller, useFormContext } from "react-hook-form"; +export default function TemplateRepositoryDescriptionField() { + const { control } = useFormContext<{ description: string }>(); + const t = useTranslations() + return + {t('template_description')} + ( +
{ backgroundColor={'grayModern.50'} fontWeight={'500'} color={'grayModern.600'} + _first={{ + borderLeftRadius: '6px' + }} + _last={{ + borderRightRadius: '6px' + }} > {t(item.title)}