From dd7cecd74741747ddb8827f7282c21490d73a951 Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Thu, 22 May 2025 14:36:14 -0700 Subject: [PATCH 01/33] Milestone 1 --- Makefile | 10 +- api/api.go | 76 + api/client/install/client.go | 129 + api/controllers/install/controller.go | 79 + api/errors.go | 7 + api/health.go | 9 + api/install.go | 58 + api/integration/install_test.go | 258 ++ api/logging.go | 35 + api/pkg/installation/config.go | 198 + api/pkg/installation/config_store.go | 40 + api/types/errors.go | 102 + api/types/errors_test.go | 53 + api/types/install.go | 39 + api/types/installation.go | 14 + cmd/installer/cli/.DS_Store | Bin 0 -> 6148 bytes cmd/installer/cli/cidr.go | 18 +- cmd/installer/cli/cidr_test.go | 13 +- cmd/installer/cli/install.go | 202 +- cmd/installer/cli/join.go | 13 +- cmd/installer/cli/join_runpreflights.go | 3 +- cmd/installer/cli/proxy.go | 171 +- go.mod | 2 +- pkg-new/config/cidr.go | 41 + .../config}/network_interface.go | 4 +- pkg-new/config/proxy.go | 184 + web/.gitignore | 38 + web/dist/assets/index-DD3QPl7Y.css | 1 + web/dist/assets/index-DPX_jAhQ.js | 127 + web/dist/index.html | 14 + web/eslint.config.js | 28 + web/index.html | 13 + web/netlify.toml | 9 + web/package-lock.json | 4093 +++++++++++++++++ web/package.json | 34 + web/postcss.config.js | 6 + web/src/App.tsx | 39 + web/src/components/common/Button.tsx | 63 + web/src/components/common/Card.tsx | 39 + web/src/components/common/Input.tsx | 78 + web/src/components/common/Logo.tsx | 11 + web/src/components/common/Select.tsx | 73 + .../prototype/PrototypeSettings.tsx | 197 + web/src/components/wizard/CompletionStep.tsx | 130 + .../components/wizard/ConfigurationStep.tsx | 476 ++ web/src/components/wizard/InstallWizard.tsx | 71 + .../components/wizard/InstallationStep.tsx | 199 + web/src/components/wizard/SetupStep.tsx | 187 + web/src/components/wizard/StepNavigation.tsx | 67 + .../wizard/ValidationInstallStep.tsx | 80 + web/src/components/wizard/ValidationStep.tsx | 221 + web/src/components/wizard/WelcomeStep.tsx | 152 + .../wizard/setup/K0sInstallation.tsx | 335 ++ .../wizard/setup/KubernetesSetup.tsx | 43 + .../components/wizard/setup/LinuxSetup.tsx | 128 + .../components/wizard/setup/NodeMetrics.tsx | 192 + .../wizard/setup/RegistryChoice.tsx | 83 + .../wizard/setup/RegistrySettings.tsx | 131 + .../wizard/setup/k0s/NodeJoinSection.tsx | 115 + .../wizard/setup/k0s/NodePreflightStatus.tsx | 66 + web/src/components/wizard/setup/types.ts | 5 + .../wizard/validation/ErrorMessage.tsx | 24 + .../validation/InstallationProgress.tsx | 33 + .../wizard/validation/LogViewer.tsx | 52 + .../wizard/validation/StatusIndicator.tsx | 54 + web/src/contexts/ConfigContext.tsx | 134 + web/src/contexts/WizardModeContext.tsx | 86 + web/src/index.css | 3 + web/src/main.tsx | 10 + web/src/types/index.ts | 69 + web/src/utils/gitea.ts | 216 + web/src/utils/infrastructure.ts | 177 + web/src/utils/installation.ts | 2 + web/src/utils/k0s.ts | 169 + web/src/utils/validation.ts | 174 + web/src/vite-env.d.ts | 1 + web/static.go | 23 + web/tailwind.config.js | 8 + web/tsconfig.app.json | 24 + web/tsconfig.json | 7 + web/tsconfig.node.json | 22 + web/vite.config.ts | 10 + 82 files changed, 10374 insertions(+), 226 deletions(-) create mode 100644 api/api.go create mode 100644 api/client/install/client.go create mode 100644 api/controllers/install/controller.go create mode 100644 api/errors.go create mode 100644 api/health.go create mode 100644 api/install.go create mode 100644 api/integration/install_test.go create mode 100644 api/logging.go create mode 100644 api/pkg/installation/config.go create mode 100644 api/pkg/installation/config_store.go create mode 100644 api/types/errors.go create mode 100644 api/types/errors_test.go create mode 100644 api/types/install.go create mode 100644 api/types/installation.go create mode 100644 cmd/installer/cli/.DS_Store create mode 100644 pkg-new/config/cidr.go rename {cmd/installer/cli => pkg-new/config}/network_interface.go (91%) create mode 100644 pkg-new/config/proxy.go create mode 100644 web/.gitignore create mode 100644 web/dist/assets/index-DD3QPl7Y.css create mode 100644 web/dist/assets/index-DPX_jAhQ.js create mode 100644 web/dist/index.html create mode 100644 web/eslint.config.js create mode 100644 web/index.html create mode 100644 web/netlify.toml create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/postcss.config.js create mode 100644 web/src/App.tsx create mode 100644 web/src/components/common/Button.tsx create mode 100644 web/src/components/common/Card.tsx create mode 100644 web/src/components/common/Input.tsx create mode 100644 web/src/components/common/Logo.tsx create mode 100644 web/src/components/common/Select.tsx create mode 100644 web/src/components/prototype/PrototypeSettings.tsx create mode 100644 web/src/components/wizard/CompletionStep.tsx create mode 100644 web/src/components/wizard/ConfigurationStep.tsx create mode 100644 web/src/components/wizard/InstallWizard.tsx create mode 100644 web/src/components/wizard/InstallationStep.tsx create mode 100644 web/src/components/wizard/SetupStep.tsx create mode 100644 web/src/components/wizard/StepNavigation.tsx create mode 100644 web/src/components/wizard/ValidationInstallStep.tsx create mode 100644 web/src/components/wizard/ValidationStep.tsx create mode 100644 web/src/components/wizard/WelcomeStep.tsx create mode 100644 web/src/components/wizard/setup/K0sInstallation.tsx create mode 100644 web/src/components/wizard/setup/KubernetesSetup.tsx create mode 100644 web/src/components/wizard/setup/LinuxSetup.tsx create mode 100644 web/src/components/wizard/setup/NodeMetrics.tsx create mode 100644 web/src/components/wizard/setup/RegistryChoice.tsx create mode 100644 web/src/components/wizard/setup/RegistrySettings.tsx create mode 100644 web/src/components/wizard/setup/k0s/NodeJoinSection.tsx create mode 100644 web/src/components/wizard/setup/k0s/NodePreflightStatus.tsx create mode 100644 web/src/components/wizard/setup/types.ts create mode 100644 web/src/components/wizard/validation/ErrorMessage.tsx create mode 100644 web/src/components/wizard/validation/InstallationProgress.tsx create mode 100644 web/src/components/wizard/validation/LogViewer.tsx create mode 100644 web/src/components/wizard/validation/StatusIndicator.tsx create mode 100644 web/src/contexts/ConfigContext.tsx create mode 100644 web/src/contexts/WizardModeContext.tsx create mode 100644 web/src/index.css create mode 100644 web/src/main.tsx create mode 100644 web/src/types/index.ts create mode 100644 web/src/utils/gitea.ts create mode 100644 web/src/utils/infrastructure.ts create mode 100644 web/src/utils/installation.ts create mode 100644 web/src/utils/k0s.ts create mode 100644 web/src/utils/validation.ts create mode 100644 web/src/vite-env.d.ts create mode 100644 web/static.go create mode 100644 web/tailwind.config.js create mode 100644 web/tsconfig.app.json create mode 100644 web/tsconfig.json create mode 100644 web/tsconfig.node.json create mode 100644 web/vite.config.ts diff --git a/Makefile b/Makefile index cb6ad9dbb..d44e45547 100644 --- a/Makefile +++ b/Makefile @@ -337,18 +337,18 @@ list-distros: .PHONY: create-node% create-node%: DISTRO = debian-bookworm create-node%: NODE_PORT = 30000 -create-node%: K0S_DATA_DIR = /var/lib/embedded-cluster/k0s +create-node%: GUIDED_EXPERIENCE_NODE_PORT = 30080 +create-node%: K0S_DATA_DIR = /var/lib/ec/k0s create-node%: @docker run -d \ --name node$* \ - --hostname node$* \ - --privileged \ - --restart=unless-stopped \ - -v $(K0S_DATA_DIR) \ + @@ -348,7 +349,9 @@ create-node%: -v $(shell pwd):/replicatedhq/embedded-cluster \ -v $(shell dirname $(shell pwd))/kots:/replicatedhq/kots \ $(if $(filter node0,node$*),-p $(NODE_PORT):$(NODE_PORT)) \ + $(if $(filter node0,node$*),-p $(GUIDED_EXPERIENCE_NODE_PORT):$(GUIDED_EXPERIENCE_NODE_PORT)) \ $(if $(filter node0,node$*),-p 30003:30003) \ + -e EC_PUBLIC_ADDRESS=localhost \ replicated/ec-distro:$(DISTRO) @$(MAKE) ssh-node$* diff --git a/api/api.go b/api/api.go new file mode 100644 index 000000000..148a269fc --- /dev/null +++ b/api/api.go @@ -0,0 +1,76 @@ +package api + +import ( + "errors" + "fmt" + "net/http" + + "github.com/gorilla/mux" + "github.com/sirupsen/logrus" + + "github.com/replicatedhq/embedded-cluster/api/controllers/install" + "github.com/replicatedhq/embedded-cluster/api/types" +) + +type API struct { + installController install.Controller + configChan chan<- *types.InstallationConfig + logger logrus.FieldLogger +} + +type APIOption func(*API) + +func WithInstallController(installController install.Controller) APIOption { + return func(a *API) { + a.installController = installController + } +} + +func WithLogger(logger logrus.FieldLogger) APIOption { + return func(a *API) { + a.logger = logger + } +} + +func WithConfigChan(configChan chan<- *types.InstallationConfig) APIOption { + return func(a *API) { + a.configChan = configChan + } +} + +func New(opts ...APIOption) (*API, error) { + api := &API{} + for _, opt := range opts { + opt(api) + } + + if api.installController == nil { + installController, err := install.NewInstallController() + if err != nil { + return nil, fmt.Errorf("new install controller: %w", err) + } + api.installController = installController + } + + if api.logger == nil { + api.logger = NewDiscardLogger() + } + + return api, nil +} + +func (a *API) RegisterRoutes(router *mux.Router) { + router.HandleFunc("/health", a.getHealth).Methods("GET") + + installRouter := router.PathPrefix("/install").Subrouter() + installRouter.HandleFunc("/", a.getInstall).Methods("GET") + installRouter.HandleFunc("/phase/set-config", a.postInstallPhaseSetConfig).Methods("POST") +} + +func handleError(w http.ResponseWriter, err error) { + var apiErr *types.APIError + if !errors.As(err, &apiErr) { + apiErr = types.NewInternalServerError(err) + } + apiErr.JSON(w) +} diff --git a/api/client/install/client.go b/api/client/install/client.go new file mode 100644 index 000000000..6118d8eb0 --- /dev/null +++ b/api/client/install/client.go @@ -0,0 +1,129 @@ +package install + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/replicatedhq/embedded-cluster/api/types" +) + +type APIError struct { + StatusCode int `json:"status_code"` + Message string `json:"message"` +} + +func (e *APIError) Error() string { + return fmt.Sprintf("status=%d, message=%q", e.StatusCode, e.Message) +} + +var defaultHTTPClient = &http.Client{ + Transport: &http.Transport{ + Proxy: nil, // This is a local client so no proxy is needed + }, +} + +type Client interface { + GetInstall() (*types.Install, error) + InstallPhaseSetConfig(config types.InstallationConfig) (*types.Install, error) +} + +type client struct { + apiURL string + httpClient *http.Client +} + +type ClientOption func(*client) + +func WithHTTPClient(httpClient *http.Client) ClientOption { + return func(c *client) { + c.httpClient = httpClient + } +} + +func New(apiURL string, opts ...ClientOption) Client { + c := &client{ + apiURL: apiURL + "/install", + } + for _, opt := range opts { + opt(c) + } + + if c.httpClient == nil { + c.httpClient = defaultHTTPClient + } + + return c +} + +func (c *client) GetInstall() (*types.Install, error) { + req, err := http.NewRequest("GET", c.apiURL, nil) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errorFromResponse(resp) + } + + var install types.Install + err = json.NewDecoder(resp.Body).Decode(&install) + if err != nil { + return nil, err + } + + return &install, nil +} + +func (c *client) InstallPhaseSetConfig(config types.InstallationConfig) (*types.Install, error) { + b, err := json.Marshal(config) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", c.apiURL+"/phase/set-config", bytes.NewBuffer(b)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errorFromResponse(resp) + } + + var install types.Install + err = json.NewDecoder(resp.Body).Decode(&install) + if err != nil { + return nil, err + } + + return &install, nil +} + +func errorFromResponse(resp *http.Response) error { + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("unexpected response: status=%d", resp.StatusCode) + } + var apiError APIError + err = json.Unmarshal(body, &apiError) + if err != nil { + return fmt.Errorf("unexpected response: status=%d, body=%q", resp.StatusCode, string(body)) + } + return &apiError +} diff --git a/api/controllers/install/controller.go b/api/controllers/install/controller.go new file mode 100644 index 000000000..e34d1f3b1 --- /dev/null +++ b/api/controllers/install/controller.go @@ -0,0 +1,79 @@ +package install + +import ( + "context" + "fmt" + + "github.com/replicatedhq/embedded-cluster/api/pkg/installation" + "github.com/replicatedhq/embedded-cluster/api/types" +) + +type Controller interface { + Get(ctx context.Context) (*types.Install, error) + SetConfig(ctx context.Context, config *types.InstallationConfig) error +} + +var _ Controller = &InstallController{} + +type InstallController struct { + configStore installation.ConfigStore +} + +type InstallControllerOption func(*InstallController) + +func WithConfigStore(configStore installation.ConfigStore) InstallControllerOption { + return func(c *InstallController) { + c.configStore = configStore + } +} + +func NewInstallController(opts ...InstallControllerOption) (*InstallController, error) { + controller := &InstallController{} + + for _, opt := range opts { + opt(controller) + } + + if controller.configStore == nil { + controller.configStore = installation.NewConfigMemoryStore() + } + + return controller, nil +} + +func (c *InstallController) Get(ctx context.Context) (*types.Install, error) { + config, err := c.configStore.Read() + if err != nil { + return nil, err + } + + err = installation.ConfigSetDefaults(config) + if err != nil { + return nil, fmt.Errorf("set defaults: %w", err) + } + + err = installation.ConfigValidate(config) + if err != nil { + return nil, fmt.Errorf("validate: %w", err) + } + + install := &types.Install{ + Config: *config, + } + + return install, nil +} + +func (c *InstallController) SetConfig(ctx context.Context, config *types.InstallationConfig) error { + err := installation.ConfigValidate(config) + if err != nil { + return fmt.Errorf("validate: %w", err) + } + + err = c.configStore.Write(*config) + if err != nil { + return fmt.Errorf("write: %w", err) + } + + return nil +} diff --git a/api/errors.go b/api/errors.go new file mode 100644 index 000000000..d76c3a351 --- /dev/null +++ b/api/errors.go @@ -0,0 +1,7 @@ +package api + +import "errors" + +var ( + ErrMethodNotAllowed = errors.New("method not allowed") +) diff --git a/api/health.go b/api/health.go new file mode 100644 index 000000000..394bbf618 --- /dev/null +++ b/api/health.go @@ -0,0 +1,9 @@ +package api + +import ( + "net/http" +) + +func (a *API) getHealth(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} diff --git a/api/install.go b/api/install.go new file mode 100644 index 000000000..c59e6a27d --- /dev/null +++ b/api/install.go @@ -0,0 +1,58 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/replicatedhq/embedded-cluster/api/types" + "github.com/sirupsen/logrus" +) + +type InstallationConfigRequest struct { + DataDirectory string `json:"dataDirectory"` +} + +func (a *API) getInstall(w http.ResponseWriter, r *http.Request) { + install, err := a.installController.Get(r.Context()) + if err != nil { + a.logger.WithFields(logrusFieldsFromRequest(r)).WithError(err). + Error("failed to get installation") + handleError(w, err) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(install) +} + +func (a *API) postInstallPhaseSetConfig(w http.ResponseWriter, r *http.Request) { + var config types.InstallationConfig + if err := json.NewDecoder(r.Body).Decode(&config); err != nil { + a.logger.WithFields(logrusFieldsFromRequest(r)).WithError(err). + Info("failed to decode installation config") + types.NewBadRequestError(err).JSON(w) + return + } + + if err := a.installController.SetConfig(r.Context(), &config); err != nil { + a.logger.WithFields(logrusFieldsFromRequest(r)).WithError(err). + Error("failed to set installation config") + handleError(w, err) + return + } + + a.getInstall(w, r) + + // TODO: this is a hack to get the config to the CLI + if a.configChan != nil { + a.configChan <- &config + } +} + +func logrusFieldsFromRequest(r *http.Request) logrus.Fields { + return logrus.Fields{ + "method": r.Method, + "path": r.URL.Path, + } +} diff --git a/api/integration/install_test.go b/api/integration/install_test.go new file mode 100644 index 000000000..db84d26ac --- /dev/null +++ b/api/integration/install_test.go @@ -0,0 +1,258 @@ +package integration + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gorilla/mux" + "github.com/replicatedhq/embedded-cluster/api" + "github.com/replicatedhq/embedded-cluster/api/controllers/install" + "github.com/replicatedhq/embedded-cluster/api/pkg/installation" + "github.com/replicatedhq/embedded-cluster/api/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInstallPhaseSetConfig(t *testing.T) { + // Create a memory store + configStore := installation.NewConfigMemoryStore() + + // Create an install controller with the memory store + installController, err := install.NewInstallController( + install.WithConfigStore(configStore), + ) + require.NoError(t, err) + + // Create the API with the install controller + apiInstance, err := api.New( + api.WithInstallController(installController), + api.WithLogger(api.NewDiscardLogger()), + ) + require.NoError(t, err) + + // Create a router and register the API routes + router := mux.NewRouter() + apiInstance.RegisterRoutes(router) + + // Test scenarios + testCases := []struct { + name string + config types.InstallationConfig + expectedStatus int + expectedError bool + }{ + { + name: "Valid config", + config: types.InstallationConfig{ + DataDirectory: "/tmp/data", + AdminConsolePort: 8000, + }, + expectedStatus: http.StatusOK, + expectedError: false, + }, + { + name: "Invalid config - port conflict", + config: types.InstallationConfig{ + DataDirectory: "/tmp/data", + AdminConsolePort: 8080, + LocalArtifactMirrorPort: 8080, // Same as AdminConsolePort + }, + expectedStatus: http.StatusBadRequest, + expectedError: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Serialize the config to JSON + configJSON, err := json.Marshal(tc.config) + require.NoError(t, err) + + // Create a request + req := httptest.NewRequest(http.MethodPost, "/install/phase/set-config", bytes.NewReader(configJSON)) + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(rec, req) + + // Check the response + assert.Equal(t, tc.expectedStatus, rec.Code) + + t.Logf("Response body: %s", rec.Body.String()) + + // Parse the response body + if tc.expectedError { + var apiError types.APIError + err = json.NewDecoder(rec.Body).Decode(&apiError) + require.NoError(t, err) + assert.Equal(t, tc.expectedStatus, apiError.StatusCode) + assert.NotEmpty(t, apiError.Message) + } else { + var install types.Install + err = json.NewDecoder(rec.Body).Decode(&install) + require.NoError(t, err) + + // Verify that the config was properly set + assert.Equal(t, tc.config.DataDirectory, install.Config.DataDirectory) + assert.Equal(t, tc.config.AdminConsolePort, install.Config.AdminConsolePort) + } + + // Also verify that the config is in the store + if !tc.expectedError { + storedConfig, err := configStore.Read() + require.NoError(t, err) + assert.Equal(t, tc.config.DataDirectory, storedConfig.DataDirectory) + assert.Equal(t, tc.config.AdminConsolePort, storedConfig.AdminConsolePort) + } + }) + } +} + +// Test that config validation errors are properly returned +func TestInstallPhaseSetConfigValidation(t *testing.T) { + // Create a memory store + configStore := installation.NewConfigMemoryStore() + + // Create an install controller with the memory store + installController, err := install.NewInstallController( + install.WithConfigStore(configStore), + ) + require.NoError(t, err) + + // Create the API with the install controller + apiInstance, err := api.New( + api.WithInstallController(installController), + api.WithLogger(api.NewDiscardLogger()), + ) + require.NoError(t, err) + + // Create a router and register the API routes + router := mux.NewRouter() + apiInstance.RegisterRoutes(router) + + // Test a validation error case with mixed CIDR settings + config := types.InstallationConfig{ + DataDirectory: "/tmp/data", + PodCIDR: "10.244.0.0/16", // Specify PodCIDR but not ServiceCIDR + } + + // Serialize the config to JSON + configJSON, err := json.Marshal(config) + require.NoError(t, err) + + // Create a request + req := httptest.NewRequest(http.MethodPost, "/install/phase/set-config", bytes.NewReader(configJSON)) + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(rec, req) + + // Check the response + assert.Equal(t, http.StatusBadRequest, rec.Code) + + t.Logf("Response body: %s", rec.Body.String()) + + // We expect a ValidationError with specific error about ServiceCIDR + var apiError types.APIError + err = json.NewDecoder(rec.Body).Decode(&apiError) + require.NoError(t, err) + assert.Contains(t, apiError.Error(), "serviceCidr") +} + +// Test that the endpoint properly handles malformed JSON +func TestInstallPhaseSetConfigBadRequest(t *testing.T) { + // Create a memory store and API + configStore := installation.NewConfigMemoryStore() + + installController, err := install.NewInstallController( + install.WithConfigStore(configStore), + ) + require.NoError(t, err) + + apiInstance, err := api.New( + api.WithInstallController(installController), + api.WithLogger(api.NewDiscardLogger()), + ) + require.NoError(t, err) + + router := mux.NewRouter() + apiInstance.RegisterRoutes(router) + + // Create a request with invalid JSON + req := httptest.NewRequest(http.MethodPost, "/install/phase/set-config", + bytes.NewReader([]byte(`{"dataDirectory": "/tmp/data", "adminConsolePort": "not-a-number"}`))) + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(rec, req) + + // Check the response + assert.Equal(t, http.StatusBadRequest, rec.Code) + + t.Logf("Response body: %s", rec.Body.String()) +} + +// Test that the server returns proper errors when the API controller fails +func TestInstallPhaseSetConfigControllerError(t *testing.T) { + // Create a mock controller that returns an error + mockController := &mockInstallController{ + setConfigError: assert.AnError, + } + + // Create the API with the mock controller + apiInstance, err := api.New( + api.WithInstallController(mockController), + api.WithLogger(api.NewDiscardLogger()), + ) + require.NoError(t, err) + + router := mux.NewRouter() + apiInstance.RegisterRoutes(router) + + // Create a valid config request + config := types.InstallationConfig{ + DataDirectory: "/tmp/data", + AdminConsolePort: 8000, + } + configJSON, err := json.Marshal(config) + require.NoError(t, err) + + // Create a request + req := httptest.NewRequest(http.MethodPost, "/install/phase/set-config", bytes.NewReader(configJSON)) + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(rec, req) + + // Check the response + assert.Equal(t, http.StatusInternalServerError, rec.Code) + + t.Logf("Response body: %s", rec.Body.String()) +} + +// Mock implementation of the install.Controller interface +type mockInstallController struct { + setConfigError error +} + +func (m *mockInstallController) Get(ctx context.Context) (*types.Install, error) { + return &types.Install{ + Config: types.InstallationConfig{}, + }, nil +} + +func (m *mockInstallController) SetConfig(ctx context.Context, config *types.InstallationConfig) error { + return m.setConfigError +} + +func (m *mockInstallController) StartInstall(ctx context.Context) error { + return nil +} diff --git a/api/logging.go b/api/logging.go new file mode 100644 index 000000000..645f5df5d --- /dev/null +++ b/api/logging.go @@ -0,0 +1,35 @@ +package api + +import ( + "fmt" + "io" + "os" + "time" + + "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" + "github.com/replicatedhq/embedded-cluster/pkg/versions" + "github.com/sirupsen/logrus" +) + +func NewLogger() (*logrus.Logger, error) { + fname := fmt.Sprintf("%s-%s.api.log", runtimeconfig.BinaryName(), time.Now().Format("20060102150405.000")) + logpath := runtimeconfig.PathToLog(fname) + logfile, err := os.OpenFile(logpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0400) + if err != nil { + return nil, fmt.Errorf("open log file: %w", err) + } + + logger := logrus.New() + logger.SetOutput(logfile) + + logger.Infof("versions: embedded-cluster=%s, k0s=%s", versions.Version, versions.K0sVersion) + logger.Infof("command line arguments: %v", os.Args) + + return logger, nil +} + +func NewDiscardLogger() *logrus.Logger { + logger := logrus.New() + logger.SetOutput(io.Discard) + return logger +} diff --git a/api/pkg/installation/config.go b/api/pkg/installation/config.go new file mode 100644 index 000000000..ec0338288 --- /dev/null +++ b/api/pkg/installation/config.go @@ -0,0 +1,198 @@ +package installation + +import ( + "errors" + "fmt" + + "github.com/replicatedhq/embedded-cluster/api/types" + ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" + newconfig "github.com/replicatedhq/embedded-cluster/pkg-new/config" + "github.com/replicatedhq/embedded-cluster/pkg/netutils" +) + +func ConfigValidate(config *types.InstallationConfig) error { + var ve *types.APIError + + if err := configValidateGlobalCIDR(config); err != nil { + ve = types.AppendFieldError(ve, "globalCidr", err) + } + + if err := configValidatePodCIDR(config); err != nil { + ve = types.AppendFieldError(ve, "podCidr", err) + } + + if err := configValidateServiceCIDR(config); err != nil { + ve = types.AppendFieldError(ve, "serviceCidr", err) + } + + if err := configValidateNetworkInterface(config); err != nil { + ve = types.AppendFieldError(ve, "networkInterface", err) + } + + if err := configValidateAdminConsolePort(config); err != nil { + ve = types.AppendFieldError(ve, "adminConsolePort", err) + } + + if err := configValidateLocalArtifactMirrorPort(config); err != nil { + ve = types.AppendFieldError(ve, "localArtifactMirrorPort", err) + } + + if err := configValidateDataDirectory(config); err != nil { + ve = types.AppendFieldError(ve, "dataDirectory", err) + } + + return ve.ErrorOrNil() +} + +func configValidateGlobalCIDR(config *types.InstallationConfig) error { + if config.GlobalCIDR == "" { + if config.PodCIDR == "" && config.ServiceCIDR == "" { + return errors.New("globalCidr is required") + } + return nil + } + + if err := netutils.ValidateCIDR(config.GlobalCIDR, 16, true); err != nil { + return err + } + + podCIDR, serviceCIDR, err := newconfig.SplitCIDR(config.GlobalCIDR) + if err != nil { + return fmt.Errorf("split globalCidr: %w", err) + } + if config.PodCIDR != "" && podCIDR != config.PodCIDR { + return errors.New("podCidr does not match globalCIDR") + } + if config.ServiceCIDR != "" && serviceCIDR != config.ServiceCIDR { + return errors.New("serviceCidr does not match globalCIDR") + } + + return nil +} + +func configValidatePodCIDR(config *types.InstallationConfig) error { + if config.ServiceCIDR != "" && config.PodCIDR == "" { + return errors.New("podCidr is required when serviceCidr is set") + } + return nil +} + +func configValidateServiceCIDR(config *types.InstallationConfig) error { + if config.PodCIDR != "" && config.ServiceCIDR == "" { + return errors.New("serviceCidr is required when podCidr is set") + } + return nil +} + +func configValidateNetworkInterface(config *types.InstallationConfig) error { + if config.NetworkInterface == "" { + return errors.New("networkInterface is required") + } + + // TODO: validate the network interface exists and is up and not loopback + return nil +} + +func configValidateAdminConsolePort(config *types.InstallationConfig) error { + if config.AdminConsolePort == 0 { + return errors.New("adminConsolePort is required") + } + + lamPort := config.LocalArtifactMirrorPort + if lamPort == 0 { + lamPort = ecv1beta1.DefaultLocalArtifactMirrorPort + } + + if config.AdminConsolePort == lamPort { + return errors.New("adminConsolePort and localArtifactMirrorPort cannot be equal") + } + + return nil +} + +func configValidateLocalArtifactMirrorPort(config *types.InstallationConfig) error { + if config.LocalArtifactMirrorPort == 0 { + return errors.New("localArtifactMirrorPort is required") + } + + acPort := config.AdminConsolePort + if acPort == 0 { + acPort = ecv1beta1.DefaultAdminConsolePort + } + + if config.LocalArtifactMirrorPort == acPort { + return errors.New("adminConsolePort and localArtifactMirrorPort cannot be equal") + } + + return nil +} + +func configValidateDataDirectory(config *types.InstallationConfig) error { + if config.DataDirectory == "" { + return errors.New("dataDirectory is required") + } + + return nil +} + +func ConfigSetDefaults(config *types.InstallationConfig) error { + if config.AdminConsolePort == 0 { + config.AdminConsolePort = ecv1beta1.DefaultAdminConsolePort + } + + if config.DataDirectory == "" { + config.DataDirectory = ecv1beta1.DefaultDataDir + } + + if config.LocalArtifactMirrorPort == 0 { + config.LocalArtifactMirrorPort = ecv1beta1.DefaultLocalArtifactMirrorPort + } + + // if a network interface was not provided, attempt to discover it + if config.NetworkInterface == "" { + autoInterface, err := newconfig.DetermineBestNetworkInterface() + if err == nil { + config.NetworkInterface = autoInterface + } + } + + if err := configSetCIDRDefaults(config); err != nil { + return fmt.Errorf("unable to set cidr defaults: %w", err) + } + + configSetProxyDefaults(config) + + return nil +} + +func configSetProxyDefaults(config *types.InstallationConfig) { + proxy := &ecv1beta1.ProxySpec{ + HTTPProxy: config.HTTPProxy, + HTTPSProxy: config.HTTPSProxy, + ProvidedNoProxy: config.NoProxy, + } + newconfig.SetProxyDefaults(proxy) + + config.HTTPProxy = proxy.HTTPProxy + config.HTTPSProxy = proxy.HTTPSProxy + config.NoProxy = proxy.ProvidedNoProxy +} + +func configSetCIDRDefaults(config *types.InstallationConfig) error { + if config.PodCIDR == "" && config.ServiceCIDR == "" { + if config.GlobalCIDR == "" { + config.GlobalCIDR = ecv1beta1.DefaultNetworkCIDR + } + + podCIDR, serviceCIDR, err := netutils.SplitNetworkCIDR(config.GlobalCIDR) + if err != nil { + return fmt.Errorf("split network cidr: %w", err) + } + config.PodCIDR = podCIDR + config.ServiceCIDR = serviceCIDR + + return nil + } + + return nil +} diff --git a/api/pkg/installation/config_store.go b/api/pkg/installation/config_store.go new file mode 100644 index 000000000..72753fe21 --- /dev/null +++ b/api/pkg/installation/config_store.go @@ -0,0 +1,40 @@ +package installation + +import ( + "sync" + + "github.com/replicatedhq/embedded-cluster/api/types" +) + +type ConfigStore interface { + Read() (*types.InstallationConfig, error) + Write(cfg types.InstallationConfig) error +} + +var _ ConfigStore = &ConfigMemoryStore{} + +type ConfigMemoryStore struct { + mu sync.RWMutex + cfg *types.InstallationConfig +} + +func NewConfigMemoryStore() *ConfigMemoryStore { + return &ConfigMemoryStore{ + cfg: &types.InstallationConfig{}, + } +} + +func (s *ConfigMemoryStore) Read() (*types.InstallationConfig, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.cfg, nil +} + +func (s *ConfigMemoryStore) Write(cfg types.InstallationConfig) error { + s.mu.Lock() + defer s.mu.Unlock() + s.cfg = &cfg + + return nil +} diff --git a/api/types/errors.go b/api/types/errors.go new file mode 100644 index 000000000..0499add60 --- /dev/null +++ b/api/types/errors.go @@ -0,0 +1,102 @@ +package types + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" +) + +type APIError struct { + StatusCode int `json:"status_code,omitempty"` + Message string `json:"message"` + Field string `json:"field,omitempty"` + Errors []*APIError `json:"errors,omitempty"` + + err error `json:"-"` +} + +func (e *APIError) Error() string { + if e == nil { + return "" + } else if len(e.Errors) == 0 { + return e.Message + } + var buf bytes.Buffer + first := true + for _, ee := range e.Errors { + if first { + first = false + } else { + buf.WriteString("; ") + } + buf.WriteString(ee.Message) + } + return fmt.Sprintf("%s: %s", e.Message, buf.String()) +} + +func (e *APIError) ErrorOrNil() error { + if e == nil || len(e.Errors) == 0 { + return nil + } + return e +} + +func (e *APIError) Unwrap() error { + return e.err +} + +func (e *APIError) JSON(w http.ResponseWriter) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(e.StatusCode) + json.NewEncoder(w).Encode(e) +} + +func NewBadRequestError(err error) *APIError { + return &APIError{ + StatusCode: http.StatusBadRequest, + Message: err.Error(), + err: err, + } +} + +func NewInternalServerError(err error) *APIError { + return &APIError{ + StatusCode: http.StatusInternalServerError, + Message: err.Error(), + err: err, + } +} + +func AppendError(apiErr *APIError, errs ...*APIError) *APIError { + var nonNilErrs []*APIError + for _, err := range errs { + if err != nil { + nonNilErrs = append(nonNilErrs, err) + } + } + if len(nonNilErrs) == 0 { + return apiErr + } + if apiErr == nil { + apiErr = NewInternalServerError(errors.New("errors")) + } + apiErr.Errors = append(apiErr.Errors, nonNilErrs...) + return apiErr +} + +func AppendFieldError(apiErr *APIError, field string, err error) *APIError { + if apiErr == nil { + apiErr = NewBadRequestError(errors.New("field errors")) + } + return AppendError(apiErr, newFieldError(field, err)) +} + +func newFieldError(field string, err error) *APIError { + return &APIError{ + Message: fmt.Sprintf("%s: %s", field, err.Error()), + Field: field, + err: err, + } +} diff --git a/api/types/errors_test.go b/api/types/errors_test.go new file mode 100644 index 000000000..2325b1127 --- /dev/null +++ b/api/types/errors_test.go @@ -0,0 +1,53 @@ +package types + +import ( + "encoding/json" + "errors" + "fmt" + "testing" +) + +func TestAppendFieldError(t *testing.T) { + tests := []struct { + name string + fe func() *APIError + want string + }{ + { + name: "empty", + fe: func() *APIError { + return nil + }, + want: "", + }, + { + name: "single error", + fe: func() *APIError { + var fe *APIError + fe = AppendFieldError(fe, "field1", errors.New("error1")) + b, _ := json.Marshal(fe) + fmt.Println(string(b)) + return fe + }, + want: "field errors: field1: error1", + }, + { + name: "multiple errors", + fe: func() *APIError { + var fe *APIError + fe = AppendFieldError(fe, "field1", errors.New("error1")) + fe = AppendFieldError(fe, "field2", errors.New("error2")) + return fe + }, + want: "field errors: field1: error1; field2: error2", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fe := tt.fe() + if got := fe.Error(); got != tt.want { + t.Errorf("APIError.Error() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/types/install.go b/api/types/install.go new file mode 100644 index 000000000..8f0116129 --- /dev/null +++ b/api/types/install.go @@ -0,0 +1,39 @@ +package types + +import ( + "time" +) + +const ( + InstallStatusRunning InstallStatus = "Running" + InstallStatusSucceeded InstallStatus = "Succeeded" + InstallStatusFailed InstallStatus = "Failed" +) + +const ( + InstallPhaseNameSettingConfig InstallPhaseName = "SettingConfig" + InstallPhaseNameStarting InstallPhaseName = "Starting" +) + +const ( + InstallPhaseStatusRunning InstallPhaseStatus = "Running" + InstallPhaseStatusSucceeded InstallPhaseStatus = "Succeeded" + InstallPhaseStatusFailed InstallPhaseStatus = "Failed" +) + +type InstallStatus string +type InstallPhaseName string +type InstallPhaseStatus string + +type Install struct { + Config InstallationConfig `json:"config"` + Status InstallStatus `json:"status"` + Phases []InstallPhase `json:"phases"` +} + +type InstallPhase struct { + Name InstallPhaseName `json:"name"` + Status InstallPhaseStatus `json:"status"` + StartedAt time.Time `json:"startedAt"` + CompletedAt time.Time `json:"completedAt"` +} diff --git a/api/types/installation.go b/api/types/installation.go new file mode 100644 index 000000000..bc6e299ab --- /dev/null +++ b/api/types/installation.go @@ -0,0 +1,14 @@ +package types + +type InstallationConfig struct { + AdminConsolePort int `json:"adminConsolePort"` + DataDirectory string `json:"dataDirectory"` + LocalArtifactMirrorPort int `json:"localArtifactMirrorPort"` + HTTPProxy string `json:"httpProxy"` + HTTPSProxy string `json:"httpsProxy"` + NoProxy string `json:"noProxy"` + NetworkInterface string `json:"networkInterface"` + PodCIDR string `json:"podCidr"` + ServiceCIDR string `json:"serviceCidr"` + GlobalCIDR string `json:"globalCidr"` +} diff --git a/cmd/installer/cli/.DS_Store b/cmd/installer/cli/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..44690a4e53e1fdc17251cd96caf7b6c372970061 GIT binary patch literal 6148 zcmeHKOG*P#5Ue&?MBHTQ@?F6jJVHD{E}$StAdH~K^{jF(k7o6UV3@cOvI*5gcfHrs z^@gd%^fUll?Oz^%C4dFp5eE-b^ZV`-yQz#3={(~ZJ6?Nizc!QV^9kqPVv7ME`25X( zA9ioUYo2_bs;m@{0#ZN921ictKq}y$yO7J#nX9yi*i^`RFncz z;8cNGZkOKwAL)P0|EDDFq<|E-Dg|u5-mF)ArRuGdm-AlR=udR7`KG&Z9TbLW$HZvI h+;}^_kD{z=zUKX2I3@<2`JfZ^GvK<&q`;*W_yO5@6{Y|H literal 0 HcmV?d00001 diff --git a/cmd/installer/cli/cidr.go b/cmd/installer/cli/cidr.go index 845d33dab..081b7ad9b 100644 --- a/cmd/installer/cli/cidr.go +++ b/cmd/installer/cli/cidr.go @@ -6,7 +6,7 @@ import ( k0sv1beta1 "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" - "github.com/replicatedhq/embedded-cluster/pkg/netutils" + newconfig "github.com/replicatedhq/embedded-cluster/pkg-new/config" "github.com/spf13/cobra" ) @@ -34,24 +34,18 @@ func validateCIDRFlags(cmd *cobra.Command) error { return fmt.Errorf("unable to get cidr flag: %w", err) } - if err := netutils.ValidateCIDR(cidr, 16, true); err != nil { + if err := newconfig.ValidateCIDR(cidr); err != nil { return err } return nil } -type CIDRConfig struct { - PodCIDR string - ServiceCIDR string - GlobalCIDR *string -} - // getCIDRConfig determines, based on the command line flags, // what are the pod and service CIDRs to be used for the cluster. If either // of --pod-cidr or --service-cidr have been set, they are used. Otherwise, // the cidr flag is split into pod and service CIDRs. -func getCIDRConfig(cmd *cobra.Command) (*CIDRConfig, error) { +func getCIDRConfig(cmd *cobra.Command) (*newconfig.CIDRConfig, error) { if cmd.Flags().Changed("pod-cidr") || cmd.Flags().Changed("service-cidr") { podCIDR, err := cmd.Flags().GetString("pod-cidr") if err != nil { @@ -61,7 +55,7 @@ func getCIDRConfig(cmd *cobra.Command) (*CIDRConfig, error) { if err != nil { return nil, fmt.Errorf("unable to get service-cidr flag: %w", err) } - return &CIDRConfig{ + return &newconfig.CIDRConfig{ PodCIDR: podCIDR, ServiceCIDR: serviceCIDR, }, nil @@ -71,11 +65,11 @@ func getCIDRConfig(cmd *cobra.Command) (*CIDRConfig, error) { if err != nil { return nil, fmt.Errorf("unable to get cidr flag: %w", err) } - podCIDR, serviceCIDR, err := netutils.SplitNetworkCIDR(globalCIDR) + podCIDR, serviceCIDR, err := newconfig.SplitCIDR(globalCIDR) if err != nil { return nil, fmt.Errorf("unable to split cidr flag: %w", err) } - return &CIDRConfig{ + return &newconfig.CIDRConfig{ PodCIDR: podCIDR, ServiceCIDR: serviceCIDR, GlobalCIDR: &globalCIDR, diff --git a/cmd/installer/cli/cidr_test.go b/cmd/installer/cli/cidr_test.go index f0d5d9a89..aba77aa76 100644 --- a/cmd/installer/cli/cidr_test.go +++ b/cmd/installer/cli/cidr_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" + newconfig "github.com/replicatedhq/embedded-cluster/pkg-new/config" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/stretchr/testify/require" @@ -14,11 +15,11 @@ func Test_getCIDRConfig(t *testing.T) { tests := []struct { name string setFlags func(flagSet *pflag.FlagSet) - expected *CIDRConfig + expected *newconfig.CIDRConfig }{ { name: "with pod and service flags", - expected: &CIDRConfig{ + expected: &newconfig.CIDRConfig{ PodCIDR: "10.0.0.0/24", ServiceCIDR: "10.1.0.0/24", GlobalCIDR: nil, @@ -30,7 +31,7 @@ func Test_getCIDRConfig(t *testing.T) { }, { name: "with pod flag", - expected: &CIDRConfig{ + expected: &newconfig.CIDRConfig{ PodCIDR: "10.0.0.0/24", ServiceCIDR: v1beta1.DefaultNetwork().ServiceCIDR, GlobalCIDR: nil, @@ -41,7 +42,7 @@ func Test_getCIDRConfig(t *testing.T) { }, { name: "with pod, service and cidr flags", - expected: &CIDRConfig{ + expected: &newconfig.CIDRConfig{ PodCIDR: "10.0.0.0/24", ServiceCIDR: "10.1.0.0/24", GlobalCIDR: nil, @@ -54,7 +55,7 @@ func Test_getCIDRConfig(t *testing.T) { }, { name: "with pod and cidr flags", - expected: &CIDRConfig{ + expected: &newconfig.CIDRConfig{ PodCIDR: "10.0.0.0/24", ServiceCIDR: v1beta1.DefaultNetwork().ServiceCIDR, GlobalCIDR: nil, @@ -66,7 +67,7 @@ func Test_getCIDRConfig(t *testing.T) { }, { name: "with cidr flag", - expected: &CIDRConfig{ + expected: &newconfig.CIDRConfig{ PodCIDR: "10.2.0.0/25", ServiceCIDR: "10.2.0.128/25", GlobalCIDR: ptr.To("10.2.0.0/24"), diff --git a/cmd/installer/cli/install.go b/cmd/installer/cli/install.go index dc3e667a2..a8e69c616 100644 --- a/cmd/installer/cli/install.go +++ b/cmd/installer/cli/install.go @@ -5,6 +5,8 @@ import ( "encoding/json" "errors" "fmt" + "net" + "net/http" "os" "path/filepath" "runtime" @@ -14,12 +16,16 @@ import ( "time" "github.com/AlecAivazis/survey/v2/terminal" + "github.com/gorilla/mux" "github.com/gosimple/slug" k0sv1beta1 "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" + "github.com/replicatedhq/embedded-cluster/api" + apitypes "github.com/replicatedhq/embedded-cluster/api/types" "github.com/replicatedhq/embedded-cluster/cmd/installer/goods" "github.com/replicatedhq/embedded-cluster/cmd/installer/kotscli" ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" "github.com/replicatedhq/embedded-cluster/kinds/types" + newconfig "github.com/replicatedhq/embedded-cluster/pkg-new/config" "github.com/replicatedhq/embedded-cluster/pkg/addons" "github.com/replicatedhq/embedded-cluster/pkg/addons/adminconsole" "github.com/replicatedhq/embedded-cluster/pkg/addons/embeddedclusteroperator" @@ -44,6 +50,7 @@ import ( "github.com/replicatedhq/embedded-cluster/pkg/spinner" "github.com/replicatedhq/embedded-cluster/pkg/support" "github.com/replicatedhq/embedded-cluster/pkg/versions" + "github.com/replicatedhq/embedded-cluster/web" kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -73,7 +80,7 @@ type InstallCmdFlags struct { license *kotsv1beta1.License proxy *ecv1beta1.ProxySpec - cidrCfg *CIDRConfig + cidrCfg *newconfig.CIDRConfig } // InstallCmd returns a cobra command for installing the embedded cluster. @@ -190,31 +197,6 @@ func preRunInstall(cmd *cobra.Command, flags *InstallCmdFlags) error { // this does not return an error - it returns the previous umask _ = syscall.Umask(0o022) - p, err := parseProxyFlags(cmd) - if err != nil { - return err - } - flags.proxy = p - - if err := validateCIDRFlags(cmd); err != nil { - return err - } - - // parse the various cidr flags to make sure we have exactly what we want - cidrCfg, err := getCIDRConfig(cmd) - if err != nil { - return fmt.Errorf("unable to determine pod and service CIDRs: %w", err) - } - flags.cidrCfg = cidrCfg - - // if a network interface flag was not provided, attempt to discover it - if flags.networkInterface == "" { - autoInterface, err := determineBestNetworkInterface() - if err == nil { - flags.networkInterface = autoInterface - } - } - // license file can be empty for restore if flags.licenseFile != "" { // validate the the license is indeed a license file @@ -238,7 +220,72 @@ func preRunInstall(cmd *cobra.Command, flags *InstallCmdFlags) error { flags.isAirgap = flags.airgapBundle != "" - runtimeconfig.ApplyFlags(cmd.Flags()) + // TODO: implement guided UI + guidedUI := true + if guidedUI { + installConfig, err := preRunInstallAPI(cmd.Context()) + if err != nil { + return fmt.Errorf("unable to start install API: %w", err) + } + + proxy, err := newconfig.GetProxySpec( + installConfig.HTTPProxy, + installConfig.HTTPSProxy, + installConfig.NoProxy, + installConfig.PodCIDR, + installConfig.ServiceCIDR, + installConfig.NetworkInterface, + nil, + ) + if err != nil { + return fmt.Errorf("unable to get proxy spec: %w", err) + } + flags.proxy = proxy + + flags.cidrCfg = &newconfig.CIDRConfig{ + PodCIDR: installConfig.PodCIDR, + ServiceCIDR: installConfig.ServiceCIDR, + } + if installConfig.GlobalCIDR != "" { + flags.cidrCfg.GlobalCIDR = &installConfig.GlobalCIDR + } + + flags.networkInterface = installConfig.NetworkInterface + flags.adminConsolePort = installConfig.AdminConsolePort + flags.dataDir = installConfig.DataDirectory + flags.localArtifactMirrorPort = installConfig.LocalArtifactMirrorPort + + runtimeconfig.SetDataDir(installConfig.DataDirectory) + runtimeconfig.SetLocalArtifactMirrorPort(installConfig.LocalArtifactMirrorPort) + runtimeconfig.SetAdminConsolePort(installConfig.AdminConsolePort) + } else { + proxy, err := parseProxyFlags(cmd) + if err != nil { + return err + } + flags.proxy = proxy + + if err := validateCIDRFlags(cmd); err != nil { + return err + } + + // parse the various cidr flags to make sure we have exactly what we want + cidrCfg, err := getCIDRConfig(cmd) + if err != nil { + return fmt.Errorf("unable to determine pod and service CIDRs: %w", err) + } + flags.cidrCfg = cidrCfg + + // if a network interface flag was not provided, attempt to discover it + if flags.networkInterface == "" { + autoInterface, err := newconfig.DetermineBestNetworkInterface() + if err == nil { + flags.networkInterface = autoInterface + } + } + + runtimeconfig.ApplyFlags(cmd.Flags()) + } os.Setenv("KUBECONFIG", runtimeconfig.PathToKubeConfig()) // this is needed for restore as well since it shares this function os.Setenv("TMPDIR", runtimeconfig.EmbeddedClusterTmpSubDir()) @@ -263,10 +310,103 @@ func preRunInstall(cmd *cobra.Command, flags *InstallCmdFlags) error { return nil } -func runInstall(ctx context.Context, name string, flags InstallCmdFlags, metricsReporter preflights.MetricsReporter) error { - if err := runInstallVerifyAndPrompt(ctx, name, &flags); err != nil { - return err +func preRunInstallAPI(ctx context.Context) (*apitypes.InstallationConfig, error) { + logger, err := api.NewLogger() + if err != nil { + logrus.Warnf("Unable to setup API logging: %v", err) + } + + listener, err := net.Listen("tcp", ":30080") // TODO: make this configurable + if err != nil { + return nil, fmt.Errorf("unable to create listener: %w", err) + } + + configChan := make(chan *apitypes.InstallationConfig) + defer close(configChan) + + apiCtx, apiCancel := context.WithCancel(ctx) + defer apiCancel() + go runInstallAPI(apiCtx, listener, logger, configChan) + + if err := waitForInstallAPI(apiCtx, listener.Addr().String()); err != nil { + return nil, fmt.Errorf("unable to wait for install API: %w", err) + } + + // TODO: fix this message to have the correct address + fmt.Println("Visit http://localhost:30080/ to configure your cluster") + + config, ok := <-configChan + if !ok { + return nil, fmt.Errorf("install API closed channel") + } + + return config, nil +} + +func runInstallAPI(ctx context.Context, listener net.Listener, logger logrus.FieldLogger, configChan chan<- *apitypes.InstallationConfig) error { + router := mux.NewRouter() + + api, err := api.New( + api.WithLogger(logger), + api.WithConfigChan(configChan), + ) + if err != nil { + return fmt.Errorf("new api: %w", err) + } + api.RegisterRoutes(router.PathPrefix("/api").Subrouter()) + + router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }).Methods("GET") + + webFs := http.FileServer(http.FS(web.Fs())) + router.PathPrefix("/").Methods("GET").Handler(webFs) + + server := &http.Server{ + Handler: router, + } + + go func() { + <-ctx.Done() + logrus.Debugf("Shutting down install API") + server.Shutdown(context.Background()) + }() + + logrus.Debugf("Install API listening on %s", listener.Addr().String()) + return server.Serve(listener) +} + +func waitForInstallAPI(ctx context.Context, addr string) error { + httpClient := http.Client{ + Timeout: 2 * time.Second, + Transport: &http.Transport{ + Proxy: nil, + }, + } + timeout := time.After(10 * time.Second) + var lastErr error + for { + select { + case <-timeout: + if lastErr != nil { + return fmt.Errorf("install API did not start in time: %w", lastErr) + } + return fmt.Errorf("install API did not start in time") + case <-time.Tick(1 * time.Second): + resp, err := httpClient.Get(fmt.Sprintf("http://%s/health", addr)) + if err != nil { + lastErr = fmt.Errorf("unable to connect to install API: %w", err) + } else if resp.StatusCode == http.StatusOK { + return nil + } + } } +} + +func runInstall(ctx context.Context, name string, flags InstallCmdFlags, metricsReporter preflights.MetricsReporter) error { + // if err := runInstallVerifyAndPrompt(ctx, name, &flags); err != nil { + // return err + // } if err := ensureAdminConsolePassword(&flags); err != nil { return err @@ -651,7 +791,7 @@ func materializeFiles(airgapBundle string) error { return nil } -func installAndStartCluster(ctx context.Context, networkInterface string, airgapBundle string, proxy *ecv1beta1.ProxySpec, cidrCfg *CIDRConfig, overrides string, mutate func(*k0sv1beta1.ClusterConfig) error) (*k0sv1beta1.ClusterConfig, error) { +func installAndStartCluster(ctx context.Context, networkInterface string, airgapBundle string, proxy *ecv1beta1.ProxySpec, cidrCfg *newconfig.CIDRConfig, overrides string, mutate func(*k0sv1beta1.ClusterConfig) error) (*k0sv1beta1.ClusterConfig, error) { loading := spinner.Start() loading.Infof("Installing node") logrus.Debugf("creating k0s configuration file") diff --git a/cmd/installer/cli/join.go b/cmd/installer/cli/join.go index 815533f51..616728b4b 100644 --- a/cmd/installer/cli/join.go +++ b/cmd/installer/cli/join.go @@ -13,6 +13,7 @@ import ( "github.com/replicatedhq/embedded-cluster/cmd/installer/goods" ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" "github.com/replicatedhq/embedded-cluster/kinds/types/join" + newconfig "github.com/replicatedhq/embedded-cluster/pkg-new/config" "github.com/replicatedhq/embedded-cluster/pkg/addons" "github.com/replicatedhq/embedded-cluster/pkg/airgap" "github.com/replicatedhq/embedded-cluster/pkg/config" @@ -114,7 +115,7 @@ func preRunJoin(flags *JoinCmdFlags) error { // if a network interface flag was not provided, attempt to discover it if flags.networkInterface == "" { - autoInterface, err := determineBestNetworkInterface() + autoInterface, err := newconfig.DetermineBestNetworkInterface() if err == nil { flags.networkInterface = autoInterface } @@ -253,9 +254,9 @@ func runJoinVerifyAndPrompt(name string, flags JoinCmdFlags, jcmd *join.JoinComm return fmt.Errorf("embedded cluster version mismatch - this binary is version %q, but the cluster is running version %q", versions.Version, jcmd.EmbeddedClusterVersion) } - setProxyEnv(jcmd.InstallationSpec.Proxy) + newconfig.SetProxyEnv(jcmd.InstallationSpec.Proxy) - proxyOK, localIP, err := checkProxyConfigForLocalIP(jcmd.InstallationSpec.Proxy, flags.networkInterface) + proxyOK, localIP, err := newconfig.CheckProxyConfigForLocalIP(jcmd.InstallationSpec.Proxy, flags.networkInterface, nil) if err != nil { return fmt.Errorf("failed to check proxy config for local IP: %w", err) } @@ -270,7 +271,7 @@ func runJoinVerifyAndPrompt(name string, flags JoinCmdFlags, jcmd *join.JoinComm return nil } -func initializeJoin(ctx context.Context, name string, jcmd *join.JoinCommandResponse, kotsAPIAddress string) (cidrCfg *CIDRConfig, err error) { +func initializeJoin(ctx context.Context, name string, jcmd *join.JoinCommandResponse, kotsAPIAddress string) (cidrCfg *newconfig.CIDRConfig, err error) { logrus.Info("") spinner := spinner.Start() spinner.Infof("Initializing") @@ -343,7 +344,7 @@ func materializeFilesForJoin(ctx context.Context, jcmd *join.JoinCommandResponse return nil } -func getJoinCIDRConfig(jcmd *join.JoinCommandResponse) (*CIDRConfig, error) { +func getJoinCIDRConfig(jcmd *join.JoinCommandResponse) (*newconfig.CIDRConfig, error) { podCIDR, serviceCIDR, err := netutils.SplitNetworkCIDR(ecv1beta1.DefaultNetworkCIDR) if err != nil { return nil, fmt.Errorf("unable to split default network CIDR: %w", err) @@ -358,7 +359,7 @@ func getJoinCIDRConfig(jcmd *join.JoinCommandResponse) (*CIDRConfig, error) { } } - return &CIDRConfig{ + return &newconfig.CIDRConfig{ PodCIDR: podCIDR, ServiceCIDR: serviceCIDR, }, nil diff --git a/cmd/installer/cli/join_runpreflights.go b/cmd/installer/cli/join_runpreflights.go index 4bf271f47..0cbe1bbf4 100644 --- a/cmd/installer/cli/join_runpreflights.go +++ b/cmd/installer/cli/join_runpreflights.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/replicatedhq/embedded-cluster/kinds/types/join" + newconfig "github.com/replicatedhq/embedded-cluster/pkg-new/config" "github.com/replicatedhq/embedded-cluster/pkg/configutils" "github.com/replicatedhq/embedded-cluster/pkg/kotsadm" "github.com/replicatedhq/embedded-cluster/pkg/netutil" @@ -92,7 +93,7 @@ func runJoinRunPreflights(ctx context.Context, name string, flags JoinCmdFlags, return nil } -func runJoinPreflights(ctx context.Context, jcmd *join.JoinCommandResponse, flags JoinCmdFlags, cidrCfg *CIDRConfig, metricsReported preflights.MetricsReporter) error { +func runJoinPreflights(ctx context.Context, jcmd *join.JoinCommandResponse, flags JoinCmdFlags, cidrCfg *newconfig.CIDRConfig, metricsReported preflights.MetricsReporter) error { nodeIP, err := netutils.FirstValidAddress(flags.networkInterface) if err != nil { return fmt.Errorf("unable to find first valid address: %w", err) diff --git a/cmd/installer/cli/proxy.go b/cmd/installer/cli/proxy.go index 94714d061..f069741d0 100644 --- a/cmd/installer/cli/proxy.go +++ b/cmd/installer/cli/proxy.go @@ -3,13 +3,10 @@ package cli import ( "fmt" "net" - "os" - "strings" ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" + newconfig "github.com/replicatedhq/embedded-cluster/pkg-new/config" "github.com/replicatedhq/embedded-cluster/pkg/netutils" - "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -26,13 +23,6 @@ func (d *defaultNetworkLookup) FirstValidIPNet(networkInterface string) (*net.IP var defaultNetworkLookupImpl NetworkLookup = &defaultNetworkLookup{} -func getNetworkIPNet(networkInterface string, lookup NetworkLookup) (*net.IPNet, error) { - if lookup == nil { - lookup = defaultNetworkLookupImpl - } - return lookup.FirstValidIPNet(networkInterface) -} - func addProxyFlags(cmd *cobra.Command) error { cmd.Flags().String("http-proxy", "", "HTTP proxy to use for the installation (overrides http_proxy/HTTP_PROXY environment variables)") cmd.Flags().String("https-proxy", "", "HTTPS proxy to use for the installation (overrides https_proxy/HTTPS_PROXY environment variables)") @@ -46,14 +36,12 @@ func parseProxyFlags(cmd *cobra.Command) (*ecv1beta1.ProxySpec, error) { if err != nil { return nil, fmt.Errorf("unable to get proxy spec from flags: %w", err) } - setProxyEnv(p) + newconfig.SetProxyEnv(p) return p, nil } func getProxySpec(cmd *cobra.Command) (*ecv1beta1.ProxySpec, error) { - proxy := &ecv1beta1.ProxySpec{} - // Command-line flags have the highest precedence httpProxy, err := cmd.Flags().GetString("http-proxy") if err != nil { @@ -67,158 +55,17 @@ func getProxySpec(cmd *cobra.Command) (*ecv1beta1.ProxySpec, error) { if err != nil { return nil, fmt.Errorf("unable to get no-proxy flag: %w", err) } - - // If flags aren't set, look for environment variables (lowercase takes precedence) - if httpProxy == "" { - if envValue := os.Getenv("http_proxy"); envValue != "" { - logrus.Debug("got http_proxy from http_proxy env var") - httpProxy = envValue - } else if envValue := os.Getenv("HTTP_PROXY"); envValue != "" { - logrus.Debug("got http_proxy from HTTP_PROXY env var") - httpProxy = envValue - } - } - - if httpsProxy == "" { - if envValue := os.Getenv("https_proxy"); envValue != "" { - logrus.Debug("got https_proxy from https_proxy env var") - httpsProxy = envValue - } else if envValue := os.Getenv("HTTPS_PROXY"); envValue != "" { - logrus.Debug("got https_proxy from HTTPS_PROXY env var") - httpsProxy = envValue - } - } - - if noProxy == "" { - if envValue := os.Getenv("no_proxy"); envValue != "" { - logrus.Debug("got no_proxy from no_proxy env var") - noProxy = envValue - } else if envValue := os.Getenv("NO_PROXY"); envValue != "" { - logrus.Debug("got no_proxy from NO_PROXY env var") - noProxy = envValue - } - } - - // Set the values on the proxy object - proxy.HTTPProxy = httpProxy - proxy.HTTPSProxy = httpsProxy - proxy.ProvidedNoProxy = noProxy - - // Now that we have all no-proxy entries (from flags/env), merge in defaults - if err := combineNoProxySuppliedValuesAndDefaults(cmd, proxy, nil); err != nil { - return nil, fmt.Errorf("unable to combine no-proxy supplied values and defaults: %w", err) - } - - if proxy.HTTPProxy == "" && proxy.HTTPSProxy == "" && proxy.NoProxy == "" { - return nil, nil - } - return proxy, nil -} - -func combineNoProxySuppliedValuesAndDefaults(cmd *cobra.Command, proxy *ecv1beta1.ProxySpec, lookup NetworkLookup) error { - if proxy.ProvidedNoProxy == "" && proxy.HTTPProxy == "" && proxy.HTTPSProxy == "" { - return nil + networkInterface, err := cmd.Flags().GetString("network-interface") + if err != nil { + return nil, fmt.Errorf("unable to get network-interface flag: %w", err) } - - // Start with runtime defaults - noProxy := runtimeconfig.DefaultNoProxy - - // Add pod and service CIDRs cidrCfg, err := getCIDRConfig(cmd) if err != nil { - return fmt.Errorf("unable to determine pod and service CIDRs: %w", err) - } - noProxy = append(noProxy, cidrCfg.PodCIDR, cidrCfg.ServiceCIDR) - - // Add user-provided no-proxy values - if proxy.ProvidedNoProxy != "" { - noProxy = append(noProxy, strings.Split(proxy.ProvidedNoProxy, ",")...) - } - - // If we have a proxy set, ensure the local IP is in the no-proxy list - if proxy.HTTPProxy != "" || proxy.HTTPSProxy != "" { - networkInterfaceFlag, err := cmd.Flags().GetString("network-interface") - if err != nil { - return fmt.Errorf("unable to get network-interface flag: %w", err) - } - - ipnet, err := getNetworkIPNet(networkInterfaceFlag, lookup) - if err != nil { - return fmt.Errorf("failed to get first valid ip net: %w", err) - } - cleanIPNet, err := cleanCIDR(ipnet) - if err != nil { - return fmt.Errorf("failed to clean subnet: %w", err) - } - - // Check if the local IP is already covered by any of the no-proxy entries - isValid, err := validateNoProxy(strings.Join(noProxy, ","), ipnet.IP.String()) - if err != nil { - return fmt.Errorf("failed to validate no-proxy: %w", err) - } else if !isValid { - logrus.Infof("The node IP (%q) is not included in the no-proxy list. Adding the network interface's subnet (%q).", ipnet.IP.String(), cleanIPNet) - noProxy = append(noProxy, cleanIPNet) - } - } - - proxy.NoProxy = strings.Join(noProxy, ",") - return nil -} - -// setProxyEnv sets the HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables based on the provided ProxySpec. -// If the provided ProxySpec is nil, this environment variables are not set. -func setProxyEnv(proxy *ecv1beta1.ProxySpec) { - if proxy == nil { - return - } - if proxy.HTTPProxy != "" { - os.Setenv("HTTP_PROXY", proxy.HTTPProxy) - } - if proxy.HTTPSProxy != "" { - os.Setenv("HTTPS_PROXY", proxy.HTTPSProxy) - } - if proxy.NoProxy != "" { - os.Setenv("NO_PROXY", proxy.NoProxy) - } -} - -func validateNoProxy(newNoProxy string, localIP string) (bool, error) { - foundLocal := false - localIPParsed := net.ParseIP(localIP) - if localIPParsed == nil { - return false, fmt.Errorf("failed to parse local IP %q", localIP) - } - - for _, oneEntry := range strings.Split(newNoProxy, ",") { - if oneEntry == localIP { - foundLocal = true - } else if strings.Contains(oneEntry, "/") { - _, ipnet, err := net.ParseCIDR(oneEntry) - if err != nil { - return false, fmt.Errorf("failed to parse CIDR within no-proxy: %w", err) - } - if ipnet.Contains(localIPParsed) { - foundLocal = true - } - } - } - - return foundLocal, nil -} - -func checkProxyConfigForLocalIP(proxy *ecv1beta1.ProxySpec, networkInterface string) (bool, string, error) { - if proxy == nil { - return true, "", nil // no proxy is fine - } - if proxy.HTTPProxy == "" && proxy.HTTPSProxy == "" { - return true, "", nil // no proxy is fine + return nil, fmt.Errorf("unable to determine pod and service CIDRs: %w", err) } - - ipnet, err := netutils.FirstValidIPNet(networkInterface) + proxy, err := newconfig.GetProxySpec(httpProxy, httpsProxy, noProxy, cidrCfg.PodCIDR, cidrCfg.ServiceCIDR, networkInterface, defaultNetworkLookupImpl) if err != nil { - return false, "", fmt.Errorf("failed to get default IPNet: %w", err) + return nil, fmt.Errorf("unable to get proxy spec: %w", err) } - - ok, err := validateNoProxy(proxy.NoProxy, ipnet.IP.String()) - return ok, ipnet.IP.String(), err + return proxy, nil } diff --git a/go.mod b/go.mod index fef6b0ddf..0e76c950a 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/go-logr/logr v1.4.2 github.com/google/go-github/v62 v62.0.0 github.com/google/uuid v1.6.0 + github.com/gorilla/mux v1.8.1 github.com/gosimple/slug v1.15.0 github.com/jedib0t/go-pretty/v6 v6.6.7 github.com/k0sproject/k0s v1.31.9-0.20250428141639-26a9908cf691 @@ -177,7 +178,6 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/gorilla/handlers v1.5.2 // indirect - github.com/gorilla/mux v1.8.1 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect diff --git a/pkg-new/config/cidr.go b/pkg-new/config/cidr.go new file mode 100644 index 000000000..11a080524 --- /dev/null +++ b/pkg-new/config/cidr.go @@ -0,0 +1,41 @@ +package cli + +import ( + "fmt" + "net" + + "github.com/replicatedhq/embedded-cluster/pkg/netutils" +) + +func ValidateCIDR(cidr string) error { + if err := netutils.ValidateCIDR(cidr, 16, true); err != nil { + return fmt.Errorf("unable to validate cidr flag: %w", err) + } + return nil +} + +type CIDRConfig struct { + PodCIDR string + ServiceCIDR string + GlobalCIDR *string +} + +// SplitCIDR takes a CIDR string and splits it into pod and service CIDRs +// to be used for the cluster. It returns a CIDRConfig containing the split CIDRs +// and the original global CIDR. +func SplitCIDR(cidr string) (string, string, error) { + podCIDR, serviceCIDR, err := netutils.SplitNetworkCIDR(cidr) + if err != nil { + return "", "", fmt.Errorf("unable to split cidr flag: %w", err) + } + return podCIDR, serviceCIDR, nil +} + +// cleanCIDR returns a `.0/x` subnet instead of a `.2/x` etc subnet +func cleanCIDR(ipnet *net.IPNet) (string, error) { + _, newNet, err := net.ParseCIDR(ipnet.String()) + if err != nil { + return "", fmt.Errorf("failed to parse local inet CIDR %q: %w", ipnet.String(), err) + } + return newNet.String(), nil +} diff --git a/cmd/installer/cli/network_interface.go b/pkg-new/config/network_interface.go similarity index 91% rename from cmd/installer/cli/network_interface.go rename to pkg-new/config/network_interface.go index 6cc86244b..8a7907e56 100644 --- a/cmd/installer/cli/network_interface.go +++ b/pkg-new/config/network_interface.go @@ -13,8 +13,8 @@ var ( ErrCannotDetermineInterfaceName = fmt.Errorf("cannot determine interface name") ) -// determineBestNetworkInterface attempts to determine the best network interface to use for the cluster. -func determineBestNetworkInterface() (string, error) { +// DetermineBestNetworkInterface attempts to determine the best network interface to use for the cluster. +func DetermineBestNetworkInterface() (string, error) { iface, err := apimachinerynet.ChooseHostInterface() if err != nil || iface == nil { diff --git a/pkg-new/config/proxy.go b/pkg-new/config/proxy.go new file mode 100644 index 000000000..566074950 --- /dev/null +++ b/pkg-new/config/proxy.go @@ -0,0 +1,184 @@ +package cli + +import ( + "fmt" + "net" + "os" + "strings" + + ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" + "github.com/replicatedhq/embedded-cluster/pkg/netutils" + "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" + "github.com/sirupsen/logrus" +) + +// NetworkLookup defines the interface for network lookups +type NetworkLookup interface { + FirstValidIPNet(networkInterface string) (*net.IPNet, error) +} + +type defaultNetworkLookup struct{} + +func (d *defaultNetworkLookup) FirstValidIPNet(networkInterface string) (*net.IPNet, error) { + return netutils.FirstValidIPNet(networkInterface) +} + +var defaultNetworkLookupImpl NetworkLookup = &defaultNetworkLookup{} + +func GetNetworkIPNet(networkInterface string, lookup NetworkLookup) (*net.IPNet, error) { + if lookup == nil { + lookup = defaultNetworkLookupImpl + } + return lookup.FirstValidIPNet(networkInterface) +} + +func GetProxySpec(httpProxy, httpsProxy, noProxy string, podCIDR string, serviceCIDR string, networkInterface string, lookup NetworkLookup) (*ecv1beta1.ProxySpec, error) { + proxy := &ecv1beta1.ProxySpec{ + HTTPProxy: httpProxy, + HTTPSProxy: httpsProxy, + ProvidedNoProxy: noProxy, + } + + SetProxyDefaults(proxy) + + // Now that we have all no-proxy entries (from flags/env), merge in defaults + if err := populateNoProxy(proxy, podCIDR, serviceCIDR, networkInterface, lookup); err != nil { + return nil, fmt.Errorf("unable to combine no-proxy supplied values and defaults: %w", err) + } + + if proxy.HTTPProxy == "" && proxy.HTTPSProxy == "" && proxy.NoProxy == "" { + return nil, nil + } + return proxy, nil +} + +func SetProxyDefaults(proxy *ecv1beta1.ProxySpec) { + if proxy.HTTPProxy == "" { + if envValue := os.Getenv("http_proxy"); envValue != "" { + // logrus.Debug("got http_proxy from http_proxy env var") + proxy.HTTPProxy = envValue + } else if envValue := os.Getenv("HTTP_PROXY"); envValue != "" { + // logrus.Debug("got http_proxy from HTTP_PROXY env var") + proxy.HTTPProxy = envValue + } + } + + if proxy.HTTPSProxy == "" { + if envValue := os.Getenv("https_proxy"); envValue != "" { + // logrus.Debug("got https_proxy from https_proxy env var") + proxy.HTTPSProxy = envValue + } else if envValue := os.Getenv("HTTPS_PROXY"); envValue != "" { + // logrus.Debug("got https_proxy from HTTPS_PROXY env var") + proxy.HTTPSProxy = envValue + } + } + + if proxy.ProvidedNoProxy == "" { + if envValue := os.Getenv("no_proxy"); envValue != "" { + // logrus.Debug("got no_proxy from no_proxy env var") + proxy.ProvidedNoProxy = envValue + } else if envValue := os.Getenv("NO_PROXY"); envValue != "" { + // logrus.Debug("got no_proxy from NO_PROXY env var") + proxy.ProvidedNoProxy = envValue + } + } +} + +func populateNoProxy(proxy *ecv1beta1.ProxySpec, podCIDR string, serviceCIDR string, networkInterface string, lookup NetworkLookup) error { + if proxy.ProvidedNoProxy == "" && proxy.HTTPProxy == "" && proxy.HTTPSProxy == "" { + return nil + } + + // Start with runtime defaults + noProxy := runtimeconfig.DefaultNoProxy + + // Add pod and service CIDRs + noProxy = append(noProxy, podCIDR, serviceCIDR) + + // Add user-provided no-proxy values + if proxy.ProvidedNoProxy != "" { + noProxy = append(noProxy, strings.Split(proxy.ProvidedNoProxy, ",")...) + } + + // If we have a proxy set, ensure the local IP is in the no-proxy list + if proxy.HTTPProxy != "" || proxy.HTTPSProxy != "" { + ipnet, err := GetNetworkIPNet(networkInterface, lookup) + if err != nil { + return fmt.Errorf("failed to get first valid ip net: %w", err) + } + cleanIPNet, err := cleanCIDR(ipnet) + if err != nil { + return fmt.Errorf("failed to clean subnet: %w", err) + } + + // Check if the local IP is already covered by any of the no-proxy entries + isValid, err := NoProxyHasLocalIP(strings.Join(noProxy, ","), ipnet.IP.String()) + if err != nil { + return fmt.Errorf("failed to validate no-proxy: %w", err) + } else if !isValid { + logrus.Debugf("The node IP (%q) is not included in the no-proxy list. Adding the network interface's subnet (%q).", ipnet.IP.String(), cleanIPNet) + noProxy = append(noProxy, cleanIPNet) + } + } + + proxy.NoProxy = strings.Join(noProxy, ",") + return nil +} + +// SetProxyEnv sets the HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables based on the provided ProxySpec. +// If the provided ProxySpec is nil, this environment variables are not set. +func SetProxyEnv(proxy *ecv1beta1.ProxySpec) { + if proxy == nil { + return + } + if proxy.HTTPProxy != "" { + os.Setenv("HTTP_PROXY", proxy.HTTPProxy) + } + if proxy.HTTPSProxy != "" { + os.Setenv("HTTPS_PROXY", proxy.HTTPSProxy) + } + if proxy.NoProxy != "" { + os.Setenv("NO_PROXY", proxy.NoProxy) + } +} + +func NoProxyHasLocalIP(noProxy string, localIP string) (bool, error) { + foundLocal := false + localIPParsed := net.ParseIP(localIP) + if localIPParsed == nil { + return false, fmt.Errorf("failed to parse local IP %q", localIP) + } + + for _, oneEntry := range strings.Split(noProxy, ",") { + if oneEntry == localIP { + foundLocal = true + } else if strings.Contains(oneEntry, "/") { + _, ipnet, err := net.ParseCIDR(oneEntry) + if err != nil { + return false, fmt.Errorf("failed to parse CIDR within no-proxy: %w", err) + } + if ipnet.Contains(localIPParsed) { + foundLocal = true + } + } + } + + return foundLocal, nil +} + +func CheckProxyConfigForLocalIP(proxy *ecv1beta1.ProxySpec, networkInterface string, lookup NetworkLookup) (bool, string, error) { + if proxy == nil { + return true, "", nil // no proxy is fine + } + if proxy.HTTPProxy == "" && proxy.HTTPSProxy == "" { + return true, "", nil // no proxy is fine + } + + ipnet, err := GetNetworkIPNet(networkInterface, lookup) + if err != nil { + return false, "", fmt.Errorf("failed to get default IPNet: %w", err) + } + + ok, err := NoProxyHasLocalIP(proxy.NoProxy, ipnet.IP.String()) + return ok, ipnet.IP.String(), err +} diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 000000000..2163071be --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,38 @@ + +# See https://help.github.com/ignore-files/ for more about ignoring files. +cypress/screenshots +cypress/videos +.Trash-* + +# dependencies +/node_modules + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json + +dist/* +!dist/README.md +/history + +#editors +.vscode/ + +logs/ + +# typescript +tsconfig.tsbuildinfo diff --git a/web/dist/assets/index-DD3QPl7Y.css b/web/dist/assets/index-DD3QPl7Y.css new file mode 100644 index 000000000..ff707e7f1 --- /dev/null +++ b/web/dist/assets/index-DD3QPl7Y.css @@ -0,0 +1 @@ +*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.inset-y-0{top:0;bottom:0}.left-0{left:0}.mx-auto{margin-left:auto;margin-right:auto}.-mb-px{margin-bottom:-1px}.-mr-1{margin-right:-.25rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-20{height:5rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-20{width:5rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-7xl{max-width:80rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1.5rem * var(--tw-space-x-reverse));margin-left:calc(1.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-t{border-top-width:1px}.border-amber-200{--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.border-gray-900{--tw-border-opacity: 1;border-color:rgb(17 24 39 / var(--tw-border-opacity))}.border-green-200{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.border-transparent{border-color:transparent}.bg-\[\#2ECC71\]{--tw-bg-opacity: 1;background-color:rgb(46 204 113 / var(--tw-bg-opacity))}.bg-\[\#3498DB\]{--tw-bg-opacity: 1;background-color:rgb(52 152 219 / var(--tw-bg-opacity))}.bg-amber-50{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity))}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pl-10{padding-left:2.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-\[\#609926\]{--tw-text-opacity: 1;color:rgb(96 153 38 / var(--tw-text-opacity))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity))}.text-amber-700{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity))}.text-amber-800{--tw-text-opacity: 1;color:rgb(146 64 14 / var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity))}.text-red-800{--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity: 1;color:rgb(133 77 14 / var(--tw-text-opacity))}.opacity-50{opacity:.5}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.hover\:border-blue-300:hover{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity))}.hover\:bg-\[\#2980B9\]:hover{--tw-bg-opacity: 1;background-color:rgb(41 128 185 / var(--tw-bg-opacity))}.hover\:bg-blue-50:hover{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity))}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-\[\#3498DB\]:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(52 152 219 / var(--tw-ring-opacity))}.focus\:ring-\[\#609926\]:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(96 153 38 / var(--tw-ring-opacity))}.focus\:ring-\[\$\{themeColor\}\]:focus{--tw-ring-color: ${themeColor}}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity))}.focus\:ring-red-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}@media (min-width: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width: 768px){.md\:flex{display:flex}.md\:flex-1{flex:1 1 0%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}}@media (min-width: 1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}} diff --git a/web/dist/assets/index-DPX_jAhQ.js b/web/dist/assets/index-DPX_jAhQ.js new file mode 100644 index 000000000..83e16d92e --- /dev/null +++ b/web/dist/assets/index-DPX_jAhQ.js @@ -0,0 +1,127 @@ +function ed(e,t){for(var n=0;nr[l]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const o of l)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(l){const o={};return l.integrity&&(o.integrity=l.integrity),l.referrerPolicy&&(o.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?o.credentials="include":l.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(l){if(l.ep)return;l.ep=!0;const o=n(l);fetch(l.href,o)}})();function td(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Ps={exports:{}},ml={},js={exports:{}},I={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var or=Symbol.for("react.element"),nd=Symbol.for("react.portal"),rd=Symbol.for("react.fragment"),ld=Symbol.for("react.strict_mode"),od=Symbol.for("react.profiler"),id=Symbol.for("react.provider"),ud=Symbol.for("react.context"),sd=Symbol.for("react.forward_ref"),ad=Symbol.for("react.suspense"),cd=Symbol.for("react.memo"),dd=Symbol.for("react.lazy"),ou=Symbol.iterator;function fd(e){return e===null||typeof e!="object"?null:(e=ou&&e[ou]||e["@@iterator"],typeof e=="function"?e:null)}var _s={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Ts=Object.assign,Ls={};function fn(e,t,n){this.props=e,this.context=t,this.refs=Ls,this.updater=n||_s}fn.prototype.isReactComponent={};fn.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};fn.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function zs(){}zs.prototype=fn.prototype;function ui(e,t,n){this.props=e,this.context=t,this.refs=Ls,this.updater=n||_s}var si=ui.prototype=new zs;si.constructor=ui;Ts(si,fn.prototype);si.isPureReactComponent=!0;var iu=Array.isArray,Is=Object.prototype.hasOwnProperty,ai={current:null},Rs={key:!0,ref:!0,__self:!0,__source:!0};function Ds(e,t,n){var r,l={},o=null,i=null;if(t!=null)for(r in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(o=""+t.key),t)Is.call(t,r)&&!Rs.hasOwnProperty(r)&&(l[r]=t[r]);var u=arguments.length-2;if(u===1)l.children=n;else if(1>>1,J=P[Q];if(0>>1;Ql(Ml,z))Stl(pr,Ml)?(P[Q]=pr,P[St]=z,Q=St):(P[Q]=Ml,P[wt]=z,Q=wt);else if(Stl(pr,z))P[Q]=pr,P[St]=z,Q=St;else break e}}return L}function l(P,L){var z=P.sortIndex-L.sortIndex;return z!==0?z:P.id-L.id}if(typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var i=Date,u=i.now();e.unstable_now=function(){return i.now()-u}}var s=[],a=[],p=1,h=null,v=3,y=!1,x=!1,w=!1,j=typeof setTimeout=="function"?setTimeout:null,d=typeof clearTimeout=="function"?clearTimeout:null,c=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function m(P){for(var L=n(a);L!==null;){if(L.callback===null)r(a);else if(L.startTime<=P)r(a),L.sortIndex=L.expirationTime,t(s,L);else break;L=n(a)}}function g(P){if(w=!1,m(P),!x)if(n(s)!==null)x=!0,Dl(C);else{var L=n(a);L!==null&&Ol(g,L.startTime-P)}}function C(P,L){x=!1,w&&(w=!1,d(T),T=-1),y=!0;var z=v;try{for(m(L),h=n(s);h!==null&&(!(h.expirationTime>L)||P&&!_e());){var Q=h.callback;if(typeof Q=="function"){h.callback=null,v=h.priorityLevel;var J=Q(h.expirationTime<=L);L=e.unstable_now(),typeof J=="function"?h.callback=J:h===n(s)&&r(s),m(L)}else r(s);h=n(s)}if(h!==null)var fr=!0;else{var wt=n(a);wt!==null&&Ol(g,wt.startTime-L),fr=!1}return fr}finally{h=null,v=z,y=!1}}var E=!1,_=null,T=-1,H=5,R=-1;function _e(){return!(e.unstable_now()-RP||125Q?(P.sortIndex=z,t(a,P),n(s)===null&&P===n(a)&&(w?(d(T),T=-1):w=!0,Ol(g,z-Q))):(P.sortIndex=J,t(s,P),x||y||(x=!0,Dl(C))),P},e.unstable_shouldYield=_e,e.unstable_wrapCallback=function(P){var L=v;return function(){var z=v;v=L;try{return P.apply(this,arguments)}finally{v=z}}}})($s);Us.exports=$s;var Nd=Us.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Pd=k,xe=Nd;function S(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),fo=Object.prototype.hasOwnProperty,jd=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,su={},au={};function _d(e){return fo.call(au,e)?!0:fo.call(su,e)?!1:jd.test(e)?au[e]=!0:(su[e]=!0,!1)}function Td(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function Ld(e,t,n,r){if(t===null||typeof t>"u"||Td(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ce(e,t,n,r,l,o,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var ne={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){ne[e]=new ce(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];ne[t]=new ce(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){ne[e]=new ce(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){ne[e]=new ce(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){ne[e]=new ce(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){ne[e]=new ce(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){ne[e]=new ce(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){ne[e]=new ce(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){ne[e]=new ce(e,5,!1,e.toLowerCase(),null,!1,!1)});var di=/[\-:]([a-z])/g;function fi(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(di,fi);ne[t]=new ce(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(di,fi);ne[t]=new ce(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(di,fi);ne[t]=new ce(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){ne[e]=new ce(e,1,!1,e.toLowerCase(),null,!1,!1)});ne.xlinkHref=new ce("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){ne[e]=new ce(e,1,!1,e.toLowerCase(),null,!0,!0)});function pi(e,t,n,r){var l=ne.hasOwnProperty(t)?ne[t]:null;(l!==null?l.type!==0:r||!(2u||l[i]!==o[u]){var s=` +`+l[i].replace(" at new "," at ");return e.displayName&&s.includes("")&&(s=s.replace("",e.displayName)),s}while(1<=i&&0<=u);break}}}finally{$l=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?Pn(e):""}function zd(e){switch(e.tag){case 5:return Pn(e.type);case 16:return Pn("Lazy");case 13:return Pn("Suspense");case 19:return Pn("SuspenseList");case 0:case 2:case 15:return e=Bl(e.type,!1),e;case 11:return e=Bl(e.type.render,!1),e;case 1:return e=Bl(e.type,!0),e;default:return""}}function vo(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Vt:return"Fragment";case Bt:return"Portal";case po:return"Profiler";case hi:return"StrictMode";case ho:return"Suspense";case mo:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case As:return(e.displayName||"Context")+".Consumer";case Vs:return(e._context.displayName||"Context")+".Provider";case mi:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case vi:return t=e.displayName||null,t!==null?t:vo(e.type)||"Memo";case et:t=e._payload,e=e._init;try{return vo(e(t))}catch{}}return null}function Id(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return vo(t);case 8:return t===hi?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function mt(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Hs(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Rd(e){var t=Hs(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(i){r=""+i,o.call(this,i)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(i){r=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function vr(e){e._valueTracker||(e._valueTracker=Rd(e))}function Qs(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Hs(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Wr(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function go(e,t){var n=t.checked;return A({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function du(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=mt(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Ks(e,t){t=t.checked,t!=null&&pi(e,"checked",t,!1)}function yo(e,t){Ks(e,t);var n=mt(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?xo(e,t.type,n):t.hasOwnProperty("defaultValue")&&xo(e,t.type,mt(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function fu(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function xo(e,t,n){(t!=="number"||Wr(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var jn=Array.isArray;function qt(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=gr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Vn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var zn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Dd=["Webkit","ms","Moz","O"];Object.keys(zn).forEach(function(e){Dd.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),zn[t]=zn[e]})});function Zs(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||zn.hasOwnProperty(e)&&zn[e]?(""+t).trim():t+"px"}function Js(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Zs(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var Od=A({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ko(e,t){if(t){if(Od[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(S(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(S(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(S(61))}if(t.style!=null&&typeof t.style!="object")throw Error(S(62))}}function Co(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Eo=null;function gi(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var No=null,bt=null,en=null;function mu(e){if(e=sr(e)){if(typeof No!="function")throw Error(S(280));var t=e.stateNode;t&&(t=wl(t),No(e.stateNode,e.type,t))}}function qs(e){bt?en?en.push(e):en=[e]:bt=e}function bs(){if(bt){var e=bt,t=en;if(en=bt=null,mu(e),t)for(e=0;e>>=0,e===0?32:31-(Kd(e)/Gd|0)|0}var yr=64,xr=4194304;function _n(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Gr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,o=e.pingedLanes,i=n&268435455;if(i!==0){var u=i&~l;u!==0?r=_n(u):(o&=i,o!==0&&(r=_n(o)))}else i=n&~l,i!==0?r=_n(i):o!==0&&(r=_n(o));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,o=t&-t,l>=o||l===16&&(o&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function ir(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Re(t),e[t]=n}function Jd(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=Rn),Eu=" ",Nu=!1;function xa(e,t){switch(e){case"keyup":return Pf.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function wa(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var At=!1;function _f(e,t){switch(e){case"compositionend":return wa(t);case"keypress":return t.which!==32?null:(Nu=!0,Eu);case"textInput":return e=t.data,e===Eu&&Nu?null:e;default:return null}}function Tf(e,t){if(At)return e==="compositionend"||!Ni&&xa(e,t)?(e=ga(),Dr=ki=lt=null,At=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Tu(n)}}function Ea(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Ea(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Na(){for(var e=window,t=Wr();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Wr(e.document)}return t}function Pi(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Uf(e){var t=Na(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Ea(n.ownerDocument.documentElement,n)){if(r!==null&&Pi(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,o=Math.min(r.start,l);r=r.end===void 0?o:Math.min(r.end,l),!e.extend&&o>r&&(l=r,r=o,o=l),l=Lu(n,o);var i=Lu(n,r);l&&i&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Wt=null,zo=null,On=null,Io=!1;function zu(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Io||Wt==null||Wt!==Wr(r)||(r=Wt,"selectionStart"in r&&Pi(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),On&&Gn(On,r)||(On=r,r=Zr(zo,"onSelect"),0Kt||(e.current=Uo[Kt],Uo[Kt]=null,Kt--)}function M(e,t){Kt++,Uo[Kt]=e.current,e.current=t}var vt={},ie=yt(vt),pe=yt(!1),Lt=vt;function on(e,t){var n=e.type.contextTypes;if(!n)return vt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},o;for(o in n)l[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function he(e){return e=e.childContextTypes,e!=null}function qr(){U(pe),U(ie)}function Uu(e,t,n){if(ie.current!==vt)throw Error(S(168));M(ie,t),M(pe,n)}function Da(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(S(108,Id(e)||"Unknown",l));return A({},n,r)}function br(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||vt,Lt=ie.current,M(ie,e),M(pe,pe.current),!0}function $u(e,t,n){var r=e.stateNode;if(!r)throw Error(S(169));n?(e=Da(e,t,Lt),r.__reactInternalMemoizedMergedChildContext=e,U(pe),U(ie),M(ie,e)):U(pe),M(pe,n)}var We=null,Sl=!1,eo=!1;function Oa(e){We===null?We=[e]:We.push(e)}function Zf(e){Sl=!0,Oa(e)}function xt(){if(!eo&&We!==null){eo=!0;var e=0,t=O;try{var n=We;for(O=1;e>=i,l-=i,He=1<<32-Re(t)+l|n<T?(H=_,_=null):H=_.sibling;var R=v(d,_,m[T],g);if(R===null){_===null&&(_=H);break}e&&_&&R.alternate===null&&t(d,_),c=o(R,c,T),E===null?C=R:E.sibling=R,E=R,_=H}if(T===m.length)return n(d,_),$&&kt(d,T),C;if(_===null){for(;TT?(H=_,_=null):H=_.sibling;var _e=v(d,_,R.value,g);if(_e===null){_===null&&(_=H);break}e&&_&&_e.alternate===null&&t(d,_),c=o(_e,c,T),E===null?C=_e:E.sibling=_e,E=_e,_=H}if(R.done)return n(d,_),$&&kt(d,T),C;if(_===null){for(;!R.done;T++,R=m.next())R=h(d,R.value,g),R!==null&&(c=o(R,c,T),E===null?C=R:E.sibling=R,E=R);return $&&kt(d,T),C}for(_=r(d,_);!R.done;T++,R=m.next())R=y(_,d,T,R.value,g),R!==null&&(e&&R.alternate!==null&&_.delete(R.key===null?T:R.key),c=o(R,c,T),E===null?C=R:E.sibling=R,E=R);return e&&_.forEach(function(vn){return t(d,vn)}),$&&kt(d,T),C}function j(d,c,m,g){if(typeof m=="object"&&m!==null&&m.type===Vt&&m.key===null&&(m=m.props.children),typeof m=="object"&&m!==null){switch(m.$$typeof){case mr:e:{for(var C=m.key,E=c;E!==null;){if(E.key===C){if(C=m.type,C===Vt){if(E.tag===7){n(d,E.sibling),c=l(E,m.props.children),c.return=d,d=c;break e}}else if(E.elementType===C||typeof C=="object"&&C!==null&&C.$$typeof===et&&Au(C)===E.type){n(d,E.sibling),c=l(E,m.props),c.ref=Cn(d,E,m),c.return=d,d=c;break e}n(d,E);break}else t(d,E);E=E.sibling}m.type===Vt?(c=_t(m.props.children,d.mode,g,m.key),c.return=d,d=c):(g=Ar(m.type,m.key,m.props,null,d.mode,g),g.ref=Cn(d,c,m),g.return=d,d=g)}return i(d);case Bt:e:{for(E=m.key;c!==null;){if(c.key===E)if(c.tag===4&&c.stateNode.containerInfo===m.containerInfo&&c.stateNode.implementation===m.implementation){n(d,c.sibling),c=l(c,m.children||[]),c.return=d,d=c;break e}else{n(d,c);break}else t(d,c);c=c.sibling}c=so(m,d.mode,g),c.return=d,d=c}return i(d);case et:return E=m._init,j(d,c,E(m._payload),g)}if(jn(m))return x(d,c,m,g);if(yn(m))return w(d,c,m,g);Pr(d,m)}return typeof m=="string"&&m!==""||typeof m=="number"?(m=""+m,c!==null&&c.tag===6?(n(d,c.sibling),c=l(c,m),c.return=d,d=c):(n(d,c),c=uo(m,d.mode,g),c.return=d,d=c),i(d)):n(d,c)}return j}var sn=$a(!0),Ba=$a(!1),nl=yt(null),rl=null,Xt=null,Li=null;function zi(){Li=Xt=rl=null}function Ii(e){var t=nl.current;U(nl),e._currentValue=t}function Vo(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function nn(e,t){rl=e,Li=Xt=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(fe=!0),e.firstContext=null)}function Pe(e){var t=e._currentValue;if(Li!==e)if(e={context:e,memoizedValue:t,next:null},Xt===null){if(rl===null)throw Error(S(308));Xt=e,rl.dependencies={lanes:0,firstContext:e}}else Xt=Xt.next=e;return t}var Nt=null;function Ri(e){Nt===null?Nt=[e]:Nt.push(e)}function Va(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Ri(t)):(n.next=l.next,l.next=n),t.interleaved=n,Xe(e,r)}function Xe(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var tt=!1;function Di(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Aa(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ke(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function dt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,D&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,Xe(e,n)}return l=r.interleaved,l===null?(t.next=t,Ri(r)):(t.next=l.next,l.next=t),r.interleaved=t,Xe(e,n)}function Mr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,xi(e,n)}}function Wu(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,o=null;if(n=n.firstBaseUpdate,n!==null){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};o===null?l=o=i:o=o.next=i,n=n.next}while(n!==null);o===null?l=o=t:o=o.next=t}else l=o=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:o,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function ll(e,t,n,r){var l=e.updateQueue;tt=!1;var o=l.firstBaseUpdate,i=l.lastBaseUpdate,u=l.shared.pending;if(u!==null){l.shared.pending=null;var s=u,a=s.next;s.next=null,i===null?o=a:i.next=a,i=s;var p=e.alternate;p!==null&&(p=p.updateQueue,u=p.lastBaseUpdate,u!==i&&(u===null?p.firstBaseUpdate=a:u.next=a,p.lastBaseUpdate=s))}if(o!==null){var h=l.baseState;i=0,p=a=s=null,u=o;do{var v=u.lane,y=u.eventTime;if((r&v)===v){p!==null&&(p=p.next={eventTime:y,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var x=e,w=u;switch(v=t,y=n,w.tag){case 1:if(x=w.payload,typeof x=="function"){h=x.call(y,h,v);break e}h=x;break e;case 3:x.flags=x.flags&-65537|128;case 0:if(x=w.payload,v=typeof x=="function"?x.call(y,h,v):x,v==null)break e;h=A({},h,v);break e;case 2:tt=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,v=l.effects,v===null?l.effects=[u]:v.push(u))}else y={eventTime:y,lane:v,tag:u.tag,payload:u.payload,callback:u.callback,next:null},p===null?(a=p=y,s=h):p=p.next=y,i|=v;if(u=u.next,u===null){if(u=l.shared.pending,u===null)break;v=u,u=v.next,v.next=null,l.lastBaseUpdate=v,l.shared.pending=null}}while(!0);if(p===null&&(s=h),l.baseState=s,l.firstBaseUpdate=a,l.lastBaseUpdate=p,t=l.shared.interleaved,t!==null){l=t;do i|=l.lane,l=l.next;while(l!==t)}else o===null&&(l.shared.lanes=0);Rt|=i,e.lanes=i,e.memoizedState=h}}function Hu(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=no.transition;no.transition={};try{e(!1),t()}finally{O=n,no.transition=r}}function oc(){return je().memoizedState}function ep(e,t,n){var r=pt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},ic(e))uc(t,n);else if(n=Va(e,t,n,r),n!==null){var l=se();De(n,e,r,l),sc(n,t,r)}}function tp(e,t,n){var r=pt(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(ic(e))uc(t,l);else{var o=e.alternate;if(e.lanes===0&&(o===null||o.lanes===0)&&(o=t.lastRenderedReducer,o!==null))try{var i=t.lastRenderedState,u=o(i,n);if(l.hasEagerState=!0,l.eagerState=u,Oe(u,i)){var s=t.interleaved;s===null?(l.next=l,Ri(t)):(l.next=s.next,s.next=l),t.interleaved=l;return}}catch{}finally{}n=Va(e,t,l,r),n!==null&&(l=se(),De(n,e,r,l),sc(n,t,r))}}function ic(e){var t=e.alternate;return e===V||t!==null&&t===V}function uc(e,t){Mn=il=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function sc(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,xi(e,n)}}var ul={readContext:Pe,useCallback:re,useContext:re,useEffect:re,useImperativeHandle:re,useInsertionEffect:re,useLayoutEffect:re,useMemo:re,useReducer:re,useRef:re,useState:re,useDebugValue:re,useDeferredValue:re,useTransition:re,useMutableSource:re,useSyncExternalStore:re,useId:re,unstable_isNewReconciler:!1},np={readContext:Pe,useCallback:function(e,t){return Fe().memoizedState=[e,t===void 0?null:t],e},useContext:Pe,useEffect:Ku,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Ur(4194308,4,ec.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ur(4194308,4,e,t)},useInsertionEffect:function(e,t){return Ur(4,2,e,t)},useMemo:function(e,t){var n=Fe();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Fe();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=ep.bind(null,V,e),[r.memoizedState,e]},useRef:function(e){var t=Fe();return e={current:e},t.memoizedState=e},useState:Qu,useDebugValue:Ai,useDeferredValue:function(e){return Fe().memoizedState=e},useTransition:function(){var e=Qu(!1),t=e[0];return e=bf.bind(null,e[1]),Fe().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=V,l=Fe();if($){if(n===void 0)throw Error(S(407));n=n()}else{if(n=t(),b===null)throw Error(S(349));It&30||Ka(r,t,n)}l.memoizedState=n;var o={value:n,getSnapshot:t};return l.queue=o,Ku(Ya.bind(null,r,o,e),[e]),r.flags|=2048,tr(9,Ga.bind(null,r,o,n,t),void 0,null),n},useId:function(){var e=Fe(),t=b.identifierPrefix;if($){var n=Qe,r=He;n=(r&~(1<<32-Re(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=bn++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=i.createElement(n,{is:r.is}):(e=i.createElement(n),n==="select"&&(i=e,r.multiple?i.multiple=!0:r.size&&(i.size=r.size))):e=i.createElementNS(e,n),e[Ue]=t,e[Zn]=r,yc(e,t,!1,!1),t.stateNode=e;e:{switch(i=Co(n,r),n){case"dialog":F("cancel",e),F("close",e),l=r;break;case"iframe":case"object":case"embed":F("load",e),l=r;break;case"video":case"audio":for(l=0;ldn&&(t.flags|=128,r=!0,En(o,!1),t.lanes=4194304)}else{if(!r)if(e=ol(i),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),En(o,!0),o.tail===null&&o.tailMode==="hidden"&&!i.alternate&&!$)return le(t),null}else 2*K()-o.renderingStartTime>dn&&n!==1073741824&&(t.flags|=128,r=!0,En(o,!1),t.lanes=4194304);o.isBackwards?(i.sibling=t.child,t.child=i):(n=o.last,n!==null?n.sibling=i:t.child=i,o.last=i)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=K(),t.sibling=null,n=B.current,M(B,r?n&1|2:n&1),t):(le(t),null);case 22:case 23:return Yi(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?ve&1073741824&&(le(t),t.subtreeFlags&6&&(t.flags|=8192)):le(t),null;case 24:return null;case 25:return null}throw Error(S(156,t.tag))}function cp(e,t){switch(_i(t),t.tag){case 1:return he(t.type)&&qr(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return an(),U(pe),U(ie),Fi(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Mi(t),null;case 13:if(U(B),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(S(340));un()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return U(B),null;case 4:return an(),null;case 10:return Ii(t.type._context),null;case 22:case 23:return Yi(),null;case 24:return null;default:return null}}var _r=!1,oe=!1,dp=typeof WeakSet=="function"?WeakSet:Set,N=null;function Zt(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){W(e,t,r)}else n.current=null}function Zo(e,t,n){try{n()}catch(r){W(e,t,r)}}var rs=!1;function fp(e,t){if(Ro=Yr,e=Na(),Pi(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break e}var i=0,u=-1,s=-1,a=0,p=0,h=e,v=null;t:for(;;){for(var y;h!==n||l!==0&&h.nodeType!==3||(u=i+l),h!==o||r!==0&&h.nodeType!==3||(s=i+r),h.nodeType===3&&(i+=h.nodeValue.length),(y=h.firstChild)!==null;)v=h,h=y;for(;;){if(h===e)break t;if(v===n&&++a===l&&(u=i),v===o&&++p===r&&(s=i),(y=h.nextSibling)!==null)break;h=v,v=h.parentNode}h=y}n=u===-1||s===-1?null:{start:u,end:s}}else n=null}n=n||{start:0,end:0}}else n=null;for(Do={focusedElem:e,selectionRange:n},Yr=!1,N=t;N!==null;)if(t=N,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,N=e;else for(;N!==null;){t=N;try{var x=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(x!==null){var w=x.memoizedProps,j=x.memoizedState,d=t.stateNode,c=d.getSnapshotBeforeUpdate(t.elementType===t.type?w:Le(t.type,w),j);d.__reactInternalSnapshotBeforeUpdate=c}break;case 3:var m=t.stateNode.containerInfo;m.nodeType===1?m.textContent="":m.nodeType===9&&m.documentElement&&m.removeChild(m.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(S(163))}}catch(g){W(t,t.return,g)}if(e=t.sibling,e!==null){e.return=t.return,N=e;break}N=t.return}return x=rs,rs=!1,x}function Fn(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var o=l.destroy;l.destroy=void 0,o!==void 0&&Zo(t,n,o)}l=l.next}while(l!==r)}}function El(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Jo(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Sc(e){var t=e.alternate;t!==null&&(e.alternate=null,Sc(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Ue],delete t[Zn],delete t[Fo],delete t[Yf],delete t[Xf])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function kc(e){return e.tag===5||e.tag===3||e.tag===4}function ls(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||kc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function qo(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Jr));else if(r!==4&&(e=e.child,e!==null))for(qo(e,t,n),e=e.sibling;e!==null;)qo(e,t,n),e=e.sibling}function bo(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(bo(e,t,n),e=e.sibling;e!==null;)bo(e,t,n),e=e.sibling}var ee=null,ze=!1;function be(e,t,n){for(n=n.child;n!==null;)Cc(e,t,n),n=n.sibling}function Cc(e,t,n){if($e&&typeof $e.onCommitFiberUnmount=="function")try{$e.onCommitFiberUnmount(vl,n)}catch{}switch(n.tag){case 5:oe||Zt(n,t);case 6:var r=ee,l=ze;ee=null,be(e,t,n),ee=r,ze=l,ee!==null&&(ze?(e=ee,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):ee.removeChild(n.stateNode));break;case 18:ee!==null&&(ze?(e=ee,n=n.stateNode,e.nodeType===8?bl(e.parentNode,n):e.nodeType===1&&bl(e,n),Qn(e)):bl(ee,n.stateNode));break;case 4:r=ee,l=ze,ee=n.stateNode.containerInfo,ze=!0,be(e,t,n),ee=r,ze=l;break;case 0:case 11:case 14:case 15:if(!oe&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var o=l,i=o.destroy;o=o.tag,i!==void 0&&(o&2||o&4)&&Zo(n,t,i),l=l.next}while(l!==r)}be(e,t,n);break;case 1:if(!oe&&(Zt(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(u){W(n,t,u)}be(e,t,n);break;case 21:be(e,t,n);break;case 22:n.mode&1?(oe=(r=oe)||n.memoizedState!==null,be(e,t,n),oe=r):be(e,t,n);break;default:be(e,t,n)}}function os(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new dp),t.forEach(function(r){var l=Sp.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function Te(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=i),r&=~o}if(r=l,r=K()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*hp(r/1960))-r,10e?16:e,ot===null)var r=!1;else{if(e=ot,ot=null,cl=0,D&6)throw Error(S(331));var l=D;for(D|=4,N=e.current;N!==null;){var o=N,i=o.child;if(N.flags&16){var u=o.deletions;if(u!==null){for(var s=0;sK()-Ki?jt(e,0):Qi|=n),me(e,t)}function zc(e,t){t===0&&(e.mode&1?(t=xr,xr<<=1,!(xr&130023424)&&(xr=4194304)):t=1);var n=se();e=Xe(e,t),e!==null&&(ir(e,t,n),me(e,n))}function wp(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),zc(e,n)}function Sp(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(S(314))}r!==null&&r.delete(t),zc(e,n)}var Ic;Ic=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||pe.current)fe=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return fe=!1,sp(e,t,n);fe=!!(e.flags&131072)}else fe=!1,$&&t.flags&1048576&&Ma(t,tl,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;$r(e,t),e=t.pendingProps;var l=on(t,ie.current);nn(t,n),l=$i(null,t,r,e,l,n);var o=Bi();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,he(r)?(o=!0,br(t)):o=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,Di(t),l.updater=Cl,t.stateNode=l,l._reactInternals=t,Wo(t,r,e,n),t=Ko(null,t,r,!0,o,n)):(t.tag=0,$&&o&&ji(t),ue(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch($r(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Cp(r),e=Le(r,e),l){case 0:t=Qo(null,t,r,e,n);break e;case 1:t=es(null,t,r,e,n);break e;case 11:t=qu(null,t,r,e,n);break e;case 14:t=bu(null,t,r,Le(r.type,e),n);break e}throw Error(S(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Le(r,l),Qo(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Le(r,l),es(e,t,r,l,n);case 3:e:{if(mc(t),e===null)throw Error(S(387));r=t.pendingProps,o=t.memoizedState,l=o.element,Aa(e,t),ll(t,r,null,n);var i=t.memoizedState;if(r=i.element,o.isDehydrated)if(o={element:r,isDehydrated:!1,cache:i.cache,pendingSuspenseBoundaries:i.pendingSuspenseBoundaries,transitions:i.transitions},t.updateQueue.baseState=o,t.memoizedState=o,t.flags&256){l=cn(Error(S(423)),t),t=ts(e,t,r,n,l);break e}else if(r!==l){l=cn(Error(S(424)),t),t=ts(e,t,r,n,l);break e}else for(ge=ct(t.stateNode.containerInfo.firstChild),ye=t,$=!0,Ie=null,n=Ba(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(un(),r===l){t=Ze(e,t,n);break e}ue(e,t,r,n)}t=t.child}return t;case 5:return Wa(t),e===null&&Bo(t),r=t.type,l=t.pendingProps,o=e!==null?e.memoizedProps:null,i=l.children,Oo(r,l)?i=null:o!==null&&Oo(r,o)&&(t.flags|=32),hc(e,t),ue(e,t,i,n),t.child;case 6:return e===null&&Bo(t),null;case 13:return vc(e,t,n);case 4:return Oi(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=sn(t,null,r,n):ue(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Le(r,l),qu(e,t,r,l,n);case 7:return ue(e,t,t.pendingProps,n),t.child;case 8:return ue(e,t,t.pendingProps.children,n),t.child;case 12:return ue(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,o=t.memoizedProps,i=l.value,M(nl,r._currentValue),r._currentValue=i,o!==null)if(Oe(o.value,i)){if(o.children===l.children&&!pe.current){t=Ze(e,t,n);break e}}else for(o=t.child,o!==null&&(o.return=t);o!==null;){var u=o.dependencies;if(u!==null){i=o.child;for(var s=u.firstContext;s!==null;){if(s.context===r){if(o.tag===1){s=Ke(-1,n&-n),s.tag=2;var a=o.updateQueue;if(a!==null){a=a.shared;var p=a.pending;p===null?s.next=s:(s.next=p.next,p.next=s),a.pending=s}}o.lanes|=n,s=o.alternate,s!==null&&(s.lanes|=n),Vo(o.return,n,t),u.lanes|=n;break}s=s.next}}else if(o.tag===10)i=o.type===t.type?null:o.child;else if(o.tag===18){if(i=o.return,i===null)throw Error(S(341));i.lanes|=n,u=i.alternate,u!==null&&(u.lanes|=n),Vo(i,n,t),i=o.sibling}else i=o.child;if(i!==null)i.return=o;else for(i=o;i!==null;){if(i===t){i=null;break}if(o=i.sibling,o!==null){o.return=i.return,i=o;break}i=i.return}o=i}ue(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,nn(t,n),l=Pe(l),r=r(l),t.flags|=1,ue(e,t,r,n),t.child;case 14:return r=t.type,l=Le(r,t.pendingProps),l=Le(r.type,l),bu(e,t,r,l,n);case 15:return fc(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Le(r,l),$r(e,t),t.tag=1,he(r)?(e=!0,br(t)):e=!1,nn(t,n),ac(t,r,l),Wo(t,r,l,n),Ko(null,t,r,!0,e,n);case 19:return gc(e,t,n);case 22:return pc(e,t,n)}throw Error(S(156,t.tag))};function Rc(e,t){return ia(e,t)}function kp(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ee(e,t,n,r){return new kp(e,t,n,r)}function Zi(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Cp(e){if(typeof e=="function")return Zi(e)?1:0;if(e!=null){if(e=e.$$typeof,e===mi)return 11;if(e===vi)return 14}return 2}function ht(e,t){var n=e.alternate;return n===null?(n=Ee(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Ar(e,t,n,r,l,o){var i=2;if(r=e,typeof e=="function")Zi(e)&&(i=1);else if(typeof e=="string")i=5;else e:switch(e){case Vt:return _t(n.children,l,o,t);case hi:i=8,l|=8;break;case po:return e=Ee(12,n,t,l|2),e.elementType=po,e.lanes=o,e;case ho:return e=Ee(13,n,t,l),e.elementType=ho,e.lanes=o,e;case mo:return e=Ee(19,n,t,l),e.elementType=mo,e.lanes=o,e;case Ws:return Pl(n,l,o,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Vs:i=10;break e;case As:i=9;break e;case mi:i=11;break e;case vi:i=14;break e;case et:i=16,r=null;break e}throw Error(S(130,e==null?e:typeof e,""))}return t=Ee(i,n,t,l),t.elementType=e,t.type=r,t.lanes=o,t}function _t(e,t,n,r){return e=Ee(7,e,r,t),e.lanes=n,e}function Pl(e,t,n,r){return e=Ee(22,e,r,t),e.elementType=Ws,e.lanes=n,e.stateNode={isHidden:!1},e}function uo(e,t,n){return e=Ee(6,e,null,t),e.lanes=n,e}function so(e,t,n){return t=Ee(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Ep(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Al(0),this.expirationTimes=Al(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Al(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Ji(e,t,n,r,l,o,i,u,s){return e=new Ep(e,t,n,u,s),t===1?(t=1,o===!0&&(t|=8)):t=0,o=Ee(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Di(o),e}function Np(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(Fc)}catch(e){console.error(e)}}Fc(),Fs.exports=we;var Lp=Fs.exports,Uc,ps=Lp;Uc=ps.createRoot,ps.hydrateRoot;/** + * @remix-run/router v1.23.0 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function rr(){return rr=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function $c(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Ip(){return Math.random().toString(36).substr(2,8)}function ms(e,t){return{usr:e.state,key:e.key,idx:t}}function li(e,t,n,r){return n===void 0&&(n=null),rr({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?mn(t):t,{state:n,key:t&&t.key||r||Ip()})}function Bc(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function mn(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function Rp(e,t,n,r){r===void 0&&(r={});let{window:l=document.defaultView,v5Compat:o=!1}=r,i=l.history,u=it.Pop,s=null,a=p();a==null&&(a=0,i.replaceState(rr({},i.state,{idx:a}),""));function p(){return(i.state||{idx:null}).idx}function h(){u=it.Pop;let j=p(),d=j==null?null:j-a;a=j,s&&s({action:u,location:w.location,delta:d})}function v(j,d){u=it.Push;let c=li(w.location,j,d);a=p()+1;let m=ms(c,a),g=w.createHref(c);try{i.pushState(m,"",g)}catch(C){if(C instanceof DOMException&&C.name==="DataCloneError")throw C;l.location.assign(g)}o&&s&&s({action:u,location:w.location,delta:1})}function y(j,d){u=it.Replace;let c=li(w.location,j,d);a=p();let m=ms(c,a),g=w.createHref(c);i.replaceState(m,"",g),o&&s&&s({action:u,location:w.location,delta:0})}function x(j){let d=l.location.origin!=="null"?l.location.origin:l.location.href,c=typeof j=="string"?j:Bc(j);return c=c.replace(/ $/,"%20"),Y(d,"No window.location.(origin|href) available to create URL for href: "+c),new URL(c,d)}let w={get action(){return u},get location(){return e(l,i)},listen(j){if(s)throw new Error("A history only accepts one active listener");return l.addEventListener(hs,h),s=j,()=>{l.removeEventListener(hs,h),s=null}},createHref(j){return t(l,j)},createURL:x,encodeLocation(j){let d=x(j);return{pathname:d.pathname,search:d.search,hash:d.hash}},push:v,replace:y,go(j){return i.go(j)}};return w}var vs;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(vs||(vs={}));function Dp(e,t,n){return n===void 0&&(n="/"),Op(e,t,n,!1)}function Op(e,t,n,r){let l=typeof t=="string"?mn(t):t,o=Wc(l.pathname||"/",n);if(o==null)return null;let i=Vc(e);Mp(i);let u=null;for(let s=0;u==null&&s{let s={relativePath:u===void 0?o.path||"":u,caseSensitive:o.caseSensitive===!0,childrenIndex:i,route:o};s.relativePath.startsWith("/")&&(Y(s.relativePath.startsWith(r),'Absolute route path "'+s.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),s.relativePath=s.relativePath.slice(r.length));let a=Tt([r,s.relativePath]),p=n.concat(s);o.children&&o.children.length>0&&(Y(o.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+a+'".')),Vc(o.children,t,p,a)),!(o.path==null&&!o.index)&&t.push({path:a,score:Wp(a,o.index),routesMeta:p})};return e.forEach((o,i)=>{var u;if(o.path===""||!((u=o.path)!=null&&u.includes("?")))l(o,i);else for(let s of Ac(o.path))l(o,i,s)}),t}function Ac(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,l=n.endsWith("?"),o=n.replace(/\?$/,"");if(r.length===0)return l?[o,""]:[o];let i=Ac(r.join("/")),u=[];return u.push(...i.map(s=>s===""?o:[o,s].join("/"))),l&&u.push(...i),u.map(s=>e.startsWith("/")&&s===""?"/":s)}function Mp(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:Hp(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const Fp=/^:[\w-]+$/,Up=3,$p=2,Bp=1,Vp=10,Ap=-2,gs=e=>e==="*";function Wp(e,t){let n=e.split("/"),r=n.length;return n.some(gs)&&(r+=Ap),t&&(r+=$p),n.filter(l=>!gs(l)).reduce((l,o)=>l+(Fp.test(o)?Up:o===""?Bp:Vp),r)}function Hp(e,t){return e.length===t.length&&e.slice(0,-1).every((r,l)=>r===t[l])?e[e.length-1]-t[t.length-1]:0}function Qp(e,t,n){let{routesMeta:r}=e,l={},o="/",i=[];for(let u=0;u{let{paramName:v,isOptional:y}=p;if(v==="*"){let w=u[h]||"";i=o.slice(0,o.length-w.length).replace(/(.)\/+$/,"$1")}const x=u[h];return y&&!x?a[v]=void 0:a[v]=(x||"").replace(/%2F/g,"/"),a},{}),pathname:o,pathnameBase:i,pattern:e}}function Kp(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),$c(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],l="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(i,u,s)=>(r.push({paramName:u,isOptional:s!=null}),s?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),l+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?l+="\\/*$":e!==""&&e!=="/"&&(l+="(?:(?=\\/|$))"),[new RegExp(l,t?void 0:"i"),r]}function Gp(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return $c(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function Wc(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}function Yp(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:l=""}=typeof e=="string"?mn(e):e;return{pathname:n?n.startsWith("/")?n:Xp(n,t):t,search:qp(r),hash:bp(l)}}function Xp(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(l=>{l===".."?n.length>1&&n.pop():l!=="."&&n.push(l)}),n.length>1?n.join("/"):"/"}function ao(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function Zp(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function Hc(e,t){let n=Zp(e);return t?n.map((r,l)=>l===n.length-1?r.pathname:r.pathnameBase):n.map(r=>r.pathnameBase)}function Qc(e,t,n,r){r===void 0&&(r=!1);let l;typeof e=="string"?l=mn(e):(l=rr({},e),Y(!l.pathname||!l.pathname.includes("?"),ao("?","pathname","search",l)),Y(!l.pathname||!l.pathname.includes("#"),ao("#","pathname","hash",l)),Y(!l.search||!l.search.includes("#"),ao("#","search","hash",l)));let o=e===""||l.pathname==="",i=o?"/":l.pathname,u;if(i==null)u=n;else{let h=t.length-1;if(!r&&i.startsWith("..")){let v=i.split("/");for(;v[0]==="..";)v.shift(),h-=1;l.pathname=v.join("/")}u=h>=0?t[h]:"/"}let s=Yp(l,u),a=i&&i!=="/"&&i.endsWith("/"),p=(o||i===".")&&n.endsWith("/");return!s.pathname.endsWith("/")&&(a||p)&&(s.pathname+="/"),s}const Tt=e=>e.join("/").replace(/\/\/+/g,"/"),Jp=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),qp=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,bp=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function eh(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const Kc=["post","put","patch","delete"];new Set(Kc);const th=["get",...Kc];new Set(th);/** + * React Router v6.30.0 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function lr(){return lr=Object.assign?Object.assign.bind():function(e){for(var t=1;t{u.current=!0}),k.useCallback(function(a,p){if(p===void 0&&(p={}),!u.current)return;if(typeof a=="number"){r.go(a);return}let h=Qc(a,JSON.parse(i),o,p.relative==="path");e==null&&t!=="/"&&(h.pathname=h.pathname==="/"?t:Tt([t,h.pathname])),(p.replace?r.replace:r.push)(h,p.state,p)},[t,r,i,o,e])}function oh(e,t){return ih(e,t)}function ih(e,t,n,r){dr()||Y(!1);let{navigator:l,static:o}=k.useContext(cr),{matches:i}=k.useContext(Ft),u=i[i.length-1],s=u?u.params:{};u&&u.pathname;let a=u?u.pathnameBase:"/";u&&u.route;let p=nu(),h;if(t){var v;let d=typeof t=="string"?mn(t):t;a==="/"||(v=d.pathname)!=null&&v.startsWith(a)||Y(!1),h=d}else h=p;let y=h.pathname||"/",x=y;if(a!=="/"){let d=a.replace(/^\//,"").split("/");x="/"+y.replace(/^\//,"").split("/").slice(d.length).join("/")}let w=!o&&n&&n.matches&&n.matches.length>0?n.matches:Dp(e,{pathname:x}),j=dh(w&&w.map(d=>Object.assign({},d,{params:Object.assign({},s,d.params),pathname:Tt([a,l.encodeLocation?l.encodeLocation(d.pathname).pathname:d.pathname]),pathnameBase:d.pathnameBase==="/"?a:Tt([a,l.encodeLocation?l.encodeLocation(d.pathnameBase).pathname:d.pathnameBase])})),i,n,r);return t&&j?k.createElement(zl.Provider,{value:{location:lr({pathname:"/",search:"",hash:"",state:null,key:"default"},h),navigationType:it.Pop}},j):j}function uh(){let e=mh(),t=eh(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,l={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"};return k.createElement(k.Fragment,null,k.createElement("h2",null,"Unexpected Application Error!"),k.createElement("h3",{style:{fontStyle:"italic"}},t),n?k.createElement("pre",{style:l},n):null,null)}const sh=k.createElement(uh,null);class ah extends k.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?k.createElement(Ft.Provider,{value:this.props.routeContext},k.createElement(Gc.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function ch(e){let{routeContext:t,match:n,children:r}=e,l=k.useContext(tu);return l&&l.static&&l.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(l.staticContext._deepestRenderedBoundaryId=n.route.id),k.createElement(Ft.Provider,{value:t},r)}function dh(e,t,n,r){var l;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var o;if(!n)return null;if(n.errors)e=n.matches;else if((o=r)!=null&&o.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let i=e,u=(l=n)==null?void 0:l.errors;if(u!=null){let p=i.findIndex(h=>h.route.id&&(u==null?void 0:u[h.route.id])!==void 0);p>=0||Y(!1),i=i.slice(0,Math.min(i.length,p+1))}let s=!1,a=-1;if(n&&r&&r.v7_partialHydration)for(let p=0;p=0?i=i.slice(0,a+1):i=[i[0]];break}}}return i.reduceRight((p,h,v)=>{let y,x=!1,w=null,j=null;n&&(y=u&&h.route.id?u[h.route.id]:void 0,w=h.route.errorElement||sh,s&&(a<0&&v===0?(x=!0,j=null):a===v&&(x=!0,j=h.route.hydrateFallbackElement||null)));let d=t.concat(i.slice(0,v+1)),c=()=>{let m;return y?m=w:x?m=j:h.route.Component?m=k.createElement(h.route.Component,null):h.route.element?m=h.route.element:m=p,k.createElement(ch,{match:h,routeContext:{outlet:p,matches:d,isDataRoute:n!=null},children:m})};return n&&(h.route.ErrorBoundary||h.route.errorElement||v===0)?k.createElement(ah,{location:n.location,revalidation:n.revalidation,component:w,error:y,children:c(),routeContext:{outlet:null,matches:d,isDataRoute:!0}}):c()},null)}var Xc=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(Xc||{}),pl=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(pl||{});function fh(e){let t=k.useContext(tu);return t||Y(!1),t}function ph(e){let t=k.useContext(nh);return t||Y(!1),t}function hh(e){let t=k.useContext(Ft);return t||Y(!1),t}function Zc(e){let t=hh(),n=t.matches[t.matches.length-1];return n.route.id||Y(!1),n.route.id}function mh(){var e;let t=k.useContext(Gc),n=ph(pl.UseRouteError),r=Zc(pl.UseRouteError);return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}function vh(){let{router:e}=fh(Xc.UseNavigateStable),t=Zc(pl.UseNavigateStable),n=k.useRef(!1);return Yc(()=>{n.current=!0}),k.useCallback(function(l,o){o===void 0&&(o={}),n.current&&(typeof l=="number"?e.navigate(l):e.navigate(l,lr({fromRouteId:t},o)))},[e,t])}function gh(e,t){e==null||e.v7_startTransition,e==null||e.v7_relativeSplatPath}function yh(e){let{to:t,replace:n,state:r,relative:l}=e;dr()||Y(!1);let{future:o,static:i}=k.useContext(cr),{matches:u}=k.useContext(Ft),{pathname:s}=nu(),a=rh(),p=Qc(t,Hc(u,o.v7_relativeSplatPath),s,l==="path"),h=JSON.stringify(p);return k.useEffect(()=>a(JSON.parse(h),{replace:n,state:r,relative:l}),[a,h,l,n,r]),null}function Ln(e){Y(!1)}function xh(e){let{basename:t="/",children:n=null,location:r,navigationType:l=it.Pop,navigator:o,static:i=!1,future:u}=e;dr()&&Y(!1);let s=t.replace(/^\/*/,"/"),a=k.useMemo(()=>({basename:s,navigator:o,static:i,future:lr({v7_relativeSplatPath:!1},u)}),[s,u,o,i]);typeof r=="string"&&(r=mn(r));let{pathname:p="/",search:h="",hash:v="",state:y=null,key:x="default"}=r,w=k.useMemo(()=>{let j=Wc(p,s);return j==null?null:{location:{pathname:j,search:h,hash:v,state:y,key:x},navigationType:l}},[s,p,h,v,y,x,l]);return w==null?null:k.createElement(cr.Provider,{value:a},k.createElement(zl.Provider,{children:n,value:w}))}function wh(e){let{children:t,location:n}=e;return oh(oi(t),n)}new Promise(()=>{});function oi(e,t){t===void 0&&(t=[]);let n=[];return k.Children.forEach(e,(r,l)=>{if(!k.isValidElement(r))return;let o=[...t,l];if(r.type===k.Fragment){n.push.apply(n,oi(r.props.children,o));return}r.type!==Ln&&Y(!1),!r.props.index||!r.props.children||Y(!1);let i={id:r.props.id||o.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(i.children=oi(r.props.children,o)),n.push(i)}),n}/** + * React Router DOM v6.30.0 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */const Sh="6";try{window.__reactRouterVersion=Sh}catch{}const kh="startTransition",xs=yd[kh];function Ch(e){let{basename:t,children:n,future:r,window:l}=e,o=k.useRef();o.current==null&&(o.current=zp({window:l,v5Compat:!0}));let i=o.current,[u,s]=k.useState({action:i.action,location:i.location}),{v7_startTransition:a}=r||{},p=k.useCallback(h=>{a&&xs?xs(()=>s(h)):s(h)},[s,a]);return k.useLayoutEffect(()=>i.listen(p),[i,p]),k.useEffect(()=>gh(r),[r]),k.createElement(xh,{basename:t,children:n,location:u.location,navigationType:u.action,navigator:i,future:r})}var ws;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(ws||(ws={}));var Ss;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Ss||(Ss={}));const ks={clusterName:"",namespace:"gitea",storageClass:"standard",domain:"",useHttps:!0,adminUsername:"giteaadmin",adminPassword:"",adminEmail:"",databaseType:"internal",dataDirectory:"/var/lib/gitea",useProxy:!1},co={skipValidation:!1,failPreflights:!1,failInstallation:!1,failHostPreflights:!1,clusterMode:"embedded",themeColor:"#316DE6",skipNodeValidation:!1,useSelfSignedCert:!1,enableMultiNode:!0,availableHostNetworkInterfaces:[{name:"eth0"},{name:"eth1"},{name:"wlan0"},{name:"docker0"}]},Cs="gitea-prototype-settings",Jc=k.createContext(void 0),Eh=({children:e})=>{const[t,n]=k.useState(ks),[r,l]=k.useState(()=>{const s=localStorage.getItem(Cs),a=s?JSON.parse(s):co;return a.themeColor||(a.themeColor=co.themeColor),a});k.useEffect(()=>{localStorage.setItem(Cs,JSON.stringify(r))},[r]);const o=s=>{n(a=>({...a,...s}))},i=s=>{l(a=>{const p={...a,...s};return p.themeColor||(p.themeColor=co.themeColor),p})},u=()=>{n(ks)};return f.jsx(Jc.Provider,{value:{config:t,prototypeSettings:r,updateConfig:o,updatePrototypeSettings:i,resetConfig:u},children:e})},qe=()=>{const e=k.useContext(Jc);if(e===void 0)throw new Error("useConfig must be used within a ConfigProvider");return e},Nh=e=>({install:{title:"Gitea Enterprise",subtitle:"Installation Wizard",welcomeTitle:"Welcome to Gitea Enterprise",welcomeDescription:`This wizard will guide you through installing Gitea Enterprise on your ${e?"Linux machine":"Kubernetes cluster"}.`,setupTitle:"Setup",setupDescription:"Set up the hosts to use for this installation.",configurationTitle:"Configuration",configurationDescription:"Configure your Gitea Enterprise installation by providing the information below.",installationTitle:"Installing Gitea Enterprise",installationDescription:"",completionTitle:"Installation Complete!",completionDescription:"Gitea Enterprise has been installed successfully.",welcomeButtonText:"Start",nextButtonText:"Next: Start Installation"},upgrade:{title:"Gitea Enterprise",subtitle:"Upgrade Wizard",welcomeTitle:"Welcome to Gitea Enterprise",welcomeDescription:`This wizard will guide you through upgrading Gitea Enterprise on your ${e?"Linux machine":"Kubernetes cluster"}.`,setupTitle:"Setup",setupDescription:"Set up the hosts to use for this installation.",configurationTitle:"Upgrade Configuration",configurationDescription:"Configure your Gitea Enterprise installation by providing the information below.",installationTitle:"Upgrading Gitea Enterprise",installationDescription:"",completionTitle:"Upgrade Complete!",completionDescription:"Gitea Enterprise has been successfully upgraded.",welcomeButtonText:"Start Upgrade",nextButtonText:"Next: Start Upgrade"}}),qc=k.createContext(void 0),Es=({children:e,mode:t})=>{const{prototypeSettings:n}=qe(),r=n.clusterMode==="embedded",l=Nh(r)[t];return f.jsx(qc.Provider,{value:{mode:t,text:l},children:e})},Il=()=>{const e=k.useContext(qc);if(e===void 0)throw new Error("useWizardMode must be used within a WizardModeProvider");return e};/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */var Ph={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const jh=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase().trim(),Ve=(e,t)=>{const n=k.forwardRef(({color:r="currentColor",size:l=24,strokeWidth:o=2,absoluteStrokeWidth:i,className:u="",children:s,...a},p)=>k.createElement("svg",{ref:p,...Ph,width:l,height:l,stroke:r,strokeWidth:i?Number(o)*24/Number(l):o,className:["lucide",`lucide-${jh(e)}`,u].join(" "),...a},[...t.map(([h,v])=>k.createElement(h,v)),...Array.isArray(s)?s:[s]]));return n.displayName=`${e}`,n};/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const _h=Ve("AlertTriangle",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z",key:"c3ski4"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Th=Ve("ChevronDown",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Lh=Ve("ChevronLeft",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const ii=Ve("ChevronRight",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const zh=Ve("ChevronUp",[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ih=Ve("ClipboardList",[["rect",{width:"8",height:"4",x:"8",y:"2",rx:"1",ry:"1",key:"tgr4d6"}],["path",{d:"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2",key:"116196"}],["path",{d:"M12 11h4",key:"1jrz19"}],["path",{d:"M12 16h4",key:"n85exb"}],["path",{d:"M8 11h.01",key:"1dfujw"}],["path",{d:"M8 16h.01",key:"18s6g9"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Rh=Ve("Download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Dh=Ve("ExternalLink",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Oh=Ve("Lock",[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Mh=Ve("Settings",[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",key:"1qme2f"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]),Fh=({currentStep:e})=>{const{mode:t}=Il(),{prototypeSettings:n}=qe(),r=n.themeColor,l=[{id:"welcome",name:"Welcome",icon:Ih},{id:"setup",name:"Setup",icon:Mh},{id:"installation",name:t==="upgrade"?"Upgrade":"Installation",icon:Rh}],o=i=>{const u=l.findIndex(a=>a.id===i.id),s=l.findIndex(a=>a.id===e);return u{const u=o(i),s=i.icon;return f.jsx("li",{className:"md:flex-1",children:f.jsxs("div",{className:"flex items-center pl-4 py-2 text-sm font-medium rounded-md",style:{backgroundColor:u==="complete"||u==="current"?`${r}1A`:"rgb(243 244 246)",color:u==="complete"||u==="current"?r:"rgb(107 114 128)",border:u==="current"?`1px solid ${r}`:void 0},children:[f.jsx("span",{className:"flex-shrink-0 w-8 h-8 flex items-center justify-center mr-3 rounded-full",style:{backgroundColor:u==="complete"||u==="current"?r:"rgb(229 231 235)",color:u==="complete"||u==="current"?"white":"rgb(107 114 128)"},children:f.jsx(s,{className:"w-5 h-5"})}),f.jsx("span",{className:"truncate",children:i.name})]})},i.id)})})})},Rl=({children:e,title:t,className:n="",titleClassName:r="",contentClassName:l="",footer:o,footerClassName:i=""})=>f.jsxs("div",{className:`bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden ${n}`,children:[t&&f.jsx("div",{className:`px-6 py-4 border-b border-gray-200 bg-gray-50 ${r}`,children:f.jsx("h3",{className:"text-lg font-medium text-gray-900",children:t})}),f.jsx("div",{className:`px-6 py-5 ${l}`,children:e}),o&&f.jsx("div",{className:`px-6 py-4 border-t border-gray-200 bg-gray-50 ${i}`,children:o})]}),hl=({children:e,onClick:t,type:n="button",variant:r="primary",size:l="md",disabled:o=!1,className:i="",icon:u})=>{const{prototypeSettings:s}=qe(),a=s.themeColor,p="inline-flex items-center justify-center font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-md",h={primary:`bg-[${a}] hover:bg-[${a}] text-white focus:ring-[${a}]`,secondary:"bg-[#3498DB] hover:bg-[#2980B9] text-white focus:ring-[#3498DB]",outline:`border border-gray-300 bg-white hover:bg-gray-50 text-gray-700 focus:ring-[${a}]`,danger:"bg-red-500 hover:bg-red-600 text-white focus:ring-red-500"},v={sm:"px-3 py-1.5 text-sm",md:"px-4 py-2 text-sm",lg:"px-5 py-2.5 text-base"},y=o?"opacity-50 cursor-not-allowed":"cursor-pointer";return f.jsxs("button",{type:n,onClick:t,disabled:o,className:`${p} ${h[r]} ${v[l]} ${y} ${i}`,style:{"--theme-color":a,backgroundColor:r==="primary"?a:void 0,borderColor:r==="outline"?"currentColor":void 0},children:[u&&f.jsx("span",{className:"mr-2",children:u}),e]})},$t=({id:e,label:t,type:n="text",value:r,onChange:l,onKeyDown:o,placeholder:i="",required:u=!1,disabled:s=!1,error:a,helpText:p,className:h="",labelClassName:v="",icon:y})=>{const{prototypeSettings:x}=qe(),w=x.themeColor;return f.jsxs("div",{className:"mb-4",children:[f.jsxs("label",{htmlFor:e,className:`block text-sm font-medium text-gray-700 mb-1 ${v}`,children:[t,u&&f.jsx("span",{className:"text-red-500 ml-1",children:"*"})]}),f.jsxs("div",{className:"relative",children:[y&&f.jsx("div",{className:"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400",children:y}),f.jsx("input",{id:e,type:n,value:r,onChange:l,onKeyDown:o,placeholder:i,disabled:s,required:u,className:`w-full px-3 py-2 ${y?"pl-10":""} border ${a?"border-red-500":"border-gray-300"} rounded-md shadow-sm focus:outline-none ${s?"bg-gray-100 text-gray-500":"bg-white"} ${h}`,style:{"--tw-ring-color":w,"--tw-ring-offset-color":w}})]}),a&&f.jsx("p",{className:"mt-1 text-sm text-red-500",children:a}),p&&!a&&f.jsx("p",{className:"mt-1 text-sm text-gray-500",children:p})]})},ru=({className:e="w-6 h-6"})=>f.jsx("img",{src:"https://upload.wikimedia.org/wikipedia/commons/b/bb/Gitea_Logo.svg",alt:"Gitea Logo",className:e}),Uh=({onNext:e})=>{const{text:t}=Il(),{prototypeSettings:n}=qe(),[r,l]=k.useState(""),[o,i]=k.useState(""),[u,s]=k.useState(!1),[a,p]=k.useState(!n.useSelfSignedCert),h=x=>{l(x.target.value),i("")},v=async()=>{if(!a){p(!0);return}if(!r.trim()){i("Password is required");return}s(!0),i("");try{const x=btoa(`admin:${r}`);(await fetch("/api/login",{method:"POST",headers:{Authorization:`Basic ${x}`}})).ok?(localStorage.setItem("auth",x),e()):i("Invalid password")}catch(x){i("Authentication failed. Please try again."),console.error("Login error:",x)}finally{s(!1)}},y=x=>{x.key==="Enter"&&a&&v()};return f.jsx("div",{className:"space-y-6",children:f.jsx(Rl,{children:f.jsxs("div",{className:"flex flex-col items-center text-center py-12",children:[f.jsx(ru,{className:"h-20 w-20 mb-6"}),f.jsx("h2",{className:"text-3xl font-bold text-gray-900",children:t.welcomeTitle}),f.jsx("p",{className:"text-xl text-gray-600 max-w-2xl mb-8",children:t.welcomeDescription}),n.useSelfSignedCert&&!a?f.jsxs(f.Fragment,{children:[f.jsx("div",{className:"w-full max-w-2xl mb-8 p-4 bg-amber-50 border border-amber-200 rounded-lg",children:f.jsxs("div",{className:"flex items-start",children:[f.jsx("div",{className:"flex-shrink-0",children:f.jsx(_h,{className:"h-5 w-5 text-amber-400"})}),f.jsxs("div",{className:"ml-3 text-left",children:[f.jsx("h3",{className:"text-sm font-medium text-amber-800",children:"Self-Signed Certificate Warning"}),f.jsxs("div",{className:"mt-2 text-sm text-amber-700",children:[f.jsx("p",{children:`When you click "Continue", you'll be redirected to a secure HTTPS connection. Your browser will show a security warning because this wizard uses a self-signed certificate.`}),f.jsx("p",{className:"mt-2",children:"To proceed:"}),f.jsxs("ol",{className:"list-decimal list-inside mt-1 space-y-1",children:[f.jsx("li",{children:`Click "Advanced" or "Show Details" in your browser's warning`}),f.jsx("li",{children:'Choose "Proceed" or "Continue" to the site'}),f.jsx("li",{children:"You'll return to this page to enter your password"})]})]})]})]})}),f.jsx(hl,{onClick:v,size:"lg",icon:f.jsx(ii,{className:"w-5 h-5"}),disabled:u,children:"Continue Securely"})]}):f.jsxs("div",{className:"w-full max-w-sm mb-8",children:[f.jsx($t,{id:"password",label:"Enter Password",type:"password",value:r,onChange:h,onKeyDown:y,error:o,required:!0,icon:f.jsx(Oh,{className:"w-5 h-5"})}),f.jsx(hl,{onClick:v,size:"lg",className:"w-full mt-4",icon:f.jsx(ii,{className:"w-5 h-5"}),disabled:u,children:t.welcomeButtonText})]})]})})})},$h=({id:e,label:t,value:n,onChange:r,options:l,required:o=!1,disabled:i=!1,error:u,helpText:s,className:a="",labelClassName:p=""})=>{const{prototypeSettings:h}=qe(),v=h.themeColor;return f.jsxs("div",{className:"mb-4",children:[f.jsxs("label",{htmlFor:e,className:`block text-sm font-medium text-gray-700 mb-1 ${p}`,children:[t,o&&f.jsx("span",{className:"text-red-500 ml-1",children:"*"})]}),f.jsx("select",{id:e,value:n,onChange:r,disabled:i,required:o,className:`w-full px-3 py-2 border ${u?"border-red-500":"border-gray-300"} rounded-md shadow-sm focus:outline-none ${i?"bg-gray-100 text-gray-500":"bg-white"} ${a}`,style:{"--tw-ring-color":v,"--tw-ring-offset-color":v},children:l.map(y=>f.jsx("option",{value:y.value,children:y.label},y.value))}),u&&f.jsx("p",{className:"mt-1 text-sm text-red-500",children:u}),s&&!u&&f.jsx("p",{className:"mt-1 text-sm text-gray-500",children:s})]})},Bh=({config:e,prototypeSettings:t,showAdvanced:n,onShowAdvancedChange:r,onInputChange:l,onSelectChange:o,availableHostNetworkInterfaces:i=[]})=>f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"space-y-4",children:[f.jsx("h2",{className:"text-lg font-medium text-gray-900",children:"System Configuration"}),f.jsx($t,{id:"dataDirectory",label:"Data Directory",value:e.dataDirectory||"",onChange:l,placeholder:"/var/lib/gitea",helpText:"Directory where Gitea will store its data (defaults to /var/lib/gitea)"})]}),f.jsxs("div",{className:"space-y-4",children:[f.jsx("h2",{className:"text-lg font-medium text-gray-900",children:"Proxy Configuration"}),f.jsxs("div",{className:"space-y-4",children:[f.jsx($t,{id:"httpProxy",label:"HTTP Proxy",value:e.httpProxy||"",onChange:l,placeholder:"http://proxy.example.com:3128",helpText:"HTTP proxy server URL"}),f.jsx($t,{id:"httpsProxy",label:"HTTPS Proxy",value:e.httpsProxy||"",onChange:l,placeholder:"https://proxy.example.com:3128",helpText:"HTTPS proxy server URL"}),f.jsx($t,{id:"noProxy",label:"Proxy Bypass List",value:e.noProxy||"",onChange:l,placeholder:"localhost,127.0.0.1,.example.com",helpText:"Comma-separated list of hosts to bypass the proxy"})]})]}),f.jsxs("div",{className:"pt-4",children:[f.jsxs("button",{type:"button",className:"flex items-center text-lg font-medium text-gray-900 mb-4",onClick:()=>r(!n),children:[n?f.jsx(Th,{className:"w-4 h-4 mr-1"}):f.jsx(zh,{className:"w-4 h-4 mr-1"}),"Advanced Settings"]}),n&&f.jsxs("div",{className:"space-y-6",children:[f.jsx($h,{id:"hostNetworkInterface",label:"Network Interface",value:e.hostNetworkInterface||"",onChange:o,options:[{value:"",label:"Select a network interface"},...i.length>0?i.map(u=>({value:u,label:u})):(t.availableHostNetworkInterfaces||[]).map(u=>({value:u.name,label:u.name}))],helpText:"Network interface to use for Gitea"}),f.jsx($t,{id:"clusterNetworkCIDR",label:"Reserved Network Range (CIDR)",value:e.clusterNetworkCIDR||"",onChange:l,placeholder:"10.244.0.0/16",helpText:"CIDR notation for the reserved network range (defaults to 10.244.0.0/16; must be /16 or larger)"})]})]})]}),Vh=({config:e,onInputChange:t})=>f.jsx("div",{className:"space-y-6",children:f.jsxs("div",{className:"space-y-4",children:[f.jsx("h3",{className:"text-lg font-medium text-gray-900",children:"Network Settings"}),f.jsx("p",{className:"text-sm text-gray-600",children:"Configure network settings for your Kubernetes cluster."}),f.jsxs("div",{className:"space-y-4",children:[f.jsx("input",{id:"hostNetworkInterface",type:"text",value:e.hostNetworkInterface||"",onChange:t,placeholder:"Network Interface",className:"w-full px-3 py-2 border border-gray-300 rounded-md"}),f.jsx("input",{id:"clusterNetworkCIDR",type:"text",value:e.clusterNetworkCIDR||"",onChange:t,placeholder:"Network CIDR (e.g., 10.244.0.0/16)",className:"w-full px-3 py-2 border border-gray-300 rounded-md"})]})]})}),Ah=({onNext:e,onBack:t})=>{const{config:n,updateConfig:r,prototypeSettings:l}=qe(),{text:o}=Il(),[i,u]=k.useState(!0),[s,a]=k.useState(!1),[p,h]=k.useState(null),[v,y]=k.useState(!0),[x,w]=k.useState([]);k.useEffect(()=>{(async()=>{try{const g=await fetch("/api/cluster-config",{headers:{...localStorage.getItem("auth")&&{Authorization:`Basic ${localStorage.getItem("auth")}`}}});if(g.ok){const E=await g.json();r({dataDirectory:E.dataDirectory||"",httpProxy:E.httpProxy||"",httpsProxy:E.httpsProxy||"",noProxy:E.noProxy||"",hostNetworkInterface:E.hostNetworkInterface||"",clusterNetworkCIDR:E.clusterNetworkCIDR||"10.244.0.0/16",useProxy:!!(E.httpProxy||E.httpsProxy)})}const C=await fetch("/api/host-network-interfaces",{headers:{...localStorage.getItem("auth")&&{Authorization:`Basic ${localStorage.getItem("auth")}`}}});if(C.ok){const E=await C.json();w(E.networkInterfaces||[])}}catch(g){console.error("Failed to fetch data:",g)}finally{y(!1)}})()},[]);const j=m=>{const{id:g,value:C}=m.target;r(g==="clusterNetworkCIDR"&&!C?{clusterNetworkCIDR:"10.244.0.0/16"}:{[g]:C})},d=m=>{const{id:g,value:C}=m.target;r({[g]:C})},c=async()=>{a(!0),h(null);try{const m={dataDirectory:n.dataDirectory,httpProxy:n.useProxy?n.httpProxy:"",httpsProxy:n.useProxy?n.httpsProxy:"",noProxy:n.useProxy?n.noProxy:"",hostNetworkInterface:n.hostNetworkInterface||"",clusterNetworkCIDR:n.clusterNetworkCIDR||"10.244.0.0/16"},g=await fetch("/api/cluster-config",{method:"POST",headers:{"Content-Type":"application/json",...localStorage.getItem("auth")&&{Authorization:`Basic ${localStorage.getItem("auth")}`}},body:JSON.stringify(m)});if(!g.ok){const C=await g.json().catch(()=>({}));throw new Error(C.message||`Server responded with ${g.status}`)}e()}catch(m){h(m instanceof Error?m.message:"Failed to setup cluster"),console.error("Cluster setup failed:",m)}finally{a(!1)}};return f.jsxs("div",{className:"space-y-6",children:[f.jsxs(Rl,{children:[f.jsxs("div",{className:"mb-6",children:[f.jsx("h2",{className:"text-2xl font-bold text-gray-900",children:o.setupTitle}),f.jsx("p",{className:"text-gray-600 mt-1",children:l.clusterMode==="embedded"?"Configure the installation settings.":o.setupDescription})]}),v?f.jsxs("div",{className:"py-4 text-center",children:[f.jsx("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto"}),f.jsx("p",{className:"mt-2 text-gray-600",children:"Loading configuration..."})]}):(l==null?void 0:l.clusterMode)==="embedded"?f.jsx(Bh,{config:n,prototypeSettings:l,showAdvanced:i,onShowAdvancedChange:u,onInputChange:j,onSelectChange:d,availableHostNetworkInterfaces:x}):f.jsx(Vh,{config:n,onInputChange:j}),p&&f.jsx("div",{className:"mt-4 p-3 bg-red-50 text-red-700 rounded-md",children:p})]}),f.jsxs("div",{className:"flex justify-between",children:[f.jsx(hl,{variant:"outline",onClick:t,icon:f.jsx(Lh,{className:"w-5 h-5"}),children:"Back"}),f.jsx(hl,{onClick:c,icon:f.jsx(ii,{className:"w-5 h-5"}),disabled:s||v,children:s?"Setting up...":o.nextButtonText})]})]})},Wh=()=>{const{config:e}=qe(),[t,n]=k.useState(!1),[r,l]=k.useState(""),[o,i]=k.useState(null),[u,s]=k.useState(!0);return k.useEffect(()=>{(async()=>{try{const p=await fetch("/api/install-cluster",{method:"POST",headers:{"Content-Type":"application/json",...localStorage.getItem("auth")&&{Authorization:`Basic ${localStorage.getItem("auth")}`}}});if(!p.ok)throw new Error(`Installation failed: ${p.statusText}`);const h=await p.json();l(h.adminConsoleUrl),n(!0),s(!1)}catch(p){i(p instanceof Error?p.message:"Failed to install cluster"),s(!1)}})()},[e.adminPassword]),f.jsx("div",{className:"space-y-6",children:f.jsx(Rl,{children:f.jsxs("div",{className:"flex flex-col items-center text-center py-12",children:[f.jsx("h2",{className:"text-2xl font-bold text-gray-900 mb-4",children:"Installing Embedded Cluster"}),u&&f.jsx("p",{className:"text-xl text-gray-600 mb-8",children:"Please wait while we complete the installation..."}),o&&f.jsxs("div",{className:"text-red-600 mb-8",children:[f.jsx("p",{className:"text-xl",children:"Installation Error"}),f.jsx("p",{children:o})]}),t&&r&&f.jsxs("a",{href:r,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",children:["Visit Admin Console",f.jsx(Dh,{className:"ml-2 -mr-1 h-5 w-5"})]})]})})})},Ns=()=>{const[e,t]=k.useState("welcome"),{text:n}=Il(),r=()=>{const i=["welcome","setup","installation"],u=i.indexOf(e);u{const i=["welcome","setup","installation"],u=i.indexOf(e);u>0&&t(i[u-1])},o=()=>{switch(e){case"welcome":return f.jsx(Uh,{onNext:r});case"setup":return f.jsx(Ah,{onNext:r,onBack:l});case"installation":return f.jsx(Wh,{});default:return null}};return f.jsxs("div",{className:"min-h-screen flex flex-col",children:[f.jsx("header",{className:"bg-white shadow-sm border-b border-gray-200",children:f.jsx("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8",children:f.jsx("div",{className:"flex justify-between items-center py-4",children:f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx(ru,{className:"h-10 w-10"}),f.jsxs("div",{children:[f.jsx("h1",{className:"text-xl font-semibold text-gray-900",children:n.title}),f.jsx("p",{className:"text-sm text-gray-500",children:n.subtitle})]})]})})})}),f.jsx("main",{className:"flex-grow",children:f.jsxs("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8",children:[f.jsx(Fh,{currentStep:e}),f.jsx("div",{className:"mt-8",children:o()})]})})]})},Hh=()=>{const{prototypeSettings:e,updatePrototypeSettings:t}=qe(),n=p=>{t({skipValidation:p.target.checked})},r=p=>{t({failPreflights:p.target.checked})},l=p=>{t({failHostPreflights:p.target.checked})},o=p=>{t({failInstallation:p.target.checked})},i=p=>{t({themeColor:p.target.value})},u=p=>{t({useSelfSignedCert:p.target.checked})},s=p=>{t({skipNodeValidation:p.target.checked})},a=p=>{t({enableMultiNode:p.target.checked})};return f.jsxs("div",{className:"min-h-screen bg-gray-50",children:[f.jsx("header",{className:"bg-white shadow-sm border-b border-gray-200",children:f.jsx("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8",children:f.jsx("div",{className:"flex justify-between items-center py-4",children:f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx(ru,{className:"h-10 w-10"}),f.jsxs("div",{children:[f.jsx("h1",{className:"text-xl font-semibold text-gray-900",children:"Prototype Settings"}),f.jsx("p",{className:"text-sm text-gray-500",children:"Configure prototype behavior"})]})]})})})}),f.jsx("main",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8",children:f.jsx(Rl,{children:f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"border-t border-gray-200 pt-6",children:[f.jsx("h2",{className:"text-lg font-medium text-gray-900 mb-4",children:"Theme Settings"}),f.jsxs("div",{className:"space-y-2",children:[f.jsx("label",{htmlFor:"themeColor",className:"block text-sm font-medium text-gray-700",children:"Theme Color"}),f.jsx("input",{type:"text",id:"themeColor",value:e.themeColor,onChange:i,placeholder:"#609926",className:"w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#609926]",pattern:"^#[0-9A-Fa-f]{6}$"}),f.jsx("p",{className:"text-sm text-gray-500",children:"Enter a hex color code (e.g., #609926)"})]})]}),f.jsxs("div",{className:"border-t border-gray-200 pt-6",children:[f.jsx("h2",{className:"text-lg font-medium text-gray-900 mb-4",children:"Security Settings"}),f.jsx("div",{className:"space-y-4",children:f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx("input",{type:"checkbox",id:"useSelfSignedCert",checked:e.useSelfSignedCert,onChange:u,className:"h-4 w-4 text-[#609926] focus:ring-[#609926] border-gray-300 rounded"}),f.jsx("label",{htmlFor:"useSelfSignedCert",className:"text-sm text-gray-700",children:"Use self-signed certificate for installer"})]})})]}),f.jsxs("div",{className:"border-t border-gray-200 pt-6",children:[f.jsx("h2",{className:"text-lg font-medium text-gray-900 mb-4",children:"Linux Installation Settings"}),f.jsx("div",{className:"space-y-4",children:f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx("input",{type:"checkbox",id:"enableMultiNode",checked:e.enableMultiNode,onChange:a,className:"h-4 w-4 text-[#609926] focus:ring-[#609926] border-gray-300 rounded"}),f.jsx("label",{htmlFor:"enableMultiNode",className:"text-sm text-gray-700",children:"Enable multi-node support for Linux installations"})]})})]}),f.jsxs("div",{className:"border-t border-gray-200 pt-6",children:[f.jsx("h2",{className:"text-lg font-medium text-gray-900 mb-4",children:"Validation Settings"}),f.jsxs("div",{className:"space-y-4",children:[f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx("input",{type:"checkbox",id:"skipValidation",checked:e.skipValidation,onChange:n,className:"h-4 w-4 text-[#609926] focus:ring-[#609926] border-gray-300 rounded"}),f.jsx("label",{htmlFor:"skipValidation",className:"text-sm text-gray-700",children:"Skip required field validation on configuration page"})]}),f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx("input",{type:"checkbox",id:"skipNodeValidation",checked:e.skipNodeValidation,onChange:s,className:"h-4 w-4 text-[#609926] focus:ring-[#609926] border-gray-300 rounded"}),f.jsx("label",{htmlFor:"skipNodeValidation",className:"text-sm text-gray-700",children:"Allow proceeding without all required hosts"})]}),f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx("input",{type:"checkbox",id:"failPreflights",checked:e.failPreflights,onChange:r,className:"h-4 w-4 text-[#609926] focus:ring-[#609926] border-gray-300 rounded"}),f.jsx("label",{htmlFor:"failPreflights",className:"text-sm text-gray-700",children:"Force preflight checks to fail"})]}),f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx("input",{type:"checkbox",id:"failHostPreflights",checked:e.failHostPreflights,onChange:l,className:"h-4 w-4 text-[#609926] focus:ring-[#609926] border-gray-300 rounded"}),f.jsx("label",{htmlFor:"failHostPreflights",className:"text-sm text-gray-700",children:"Force host preflight checks to fail"})]}),f.jsxs("div",{className:"flex items-center space-x-3",children:[f.jsx("input",{type:"checkbox",id:"failInstallation",checked:e.failInstallation,onChange:o,className:"h-4 w-4 text-[#609926] focus:ring-[#609926] border-gray-300 rounded"}),f.jsx("label",{htmlFor:"failInstallation",className:"text-sm text-gray-700",children:"Simulate installation failure"})]})]}),f.jsx("p",{className:"text-sm text-gray-500 mt-4",children:"These settings affect how the installer behaves during development and testing."})]})]})})})]})};function Qh(){return f.jsx(Eh,{children:f.jsx("div",{className:"min-h-screen bg-gray-50 text-gray-900 font-sans",children:f.jsx(Ch,{children:f.jsxs(wh,{children:[f.jsx(Ln,{path:"/prototype",element:f.jsx(Hh,{})}),f.jsx(Ln,{path:"/",element:f.jsx(Es,{mode:"install",children:f.jsx(Ns,{})})}),f.jsx(Ln,{path:"/upgrade",element:f.jsx(Es,{mode:"upgrade",children:f.jsx(Ns,{})})}),f.jsx(Ln,{path:"*",element:f.jsx(yh,{to:"/",replace:!0})})]})})})})}Uc(document.getElementById("root")).render(f.jsx(k.StrictMode,{children:f.jsx(Qh,{})})); diff --git a/web/dist/index.html b/web/dist/index.html new file mode 100644 index 000000000..632c93283 --- /dev/null +++ b/web/dist/index.html @@ -0,0 +1,14 @@ + + + + + + + Gitea Enterprise Installer + + + + +
+ + diff --git a/web/eslint.config.js b/web/eslint.config.js new file mode 100644 index 000000000..82c2e20cc --- /dev/null +++ b/web/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + } +); diff --git a/web/index.html b/web/index.html new file mode 100644 index 000000000..f61af318d --- /dev/null +++ b/web/index.html @@ -0,0 +1,13 @@ + + + + + + + Gitea Enterprise Installer + + +
+ + + diff --git a/web/netlify.toml b/web/netlify.toml new file mode 100644 index 000000000..35628dbee --- /dev/null +++ b/web/netlify.toml @@ -0,0 +1,9 @@ +[build] + publish = "dist" + command = "npm run build" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 + force = true \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 000000000..7fbd20639 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,4093 @@ +{ + "name": "vite-react-typescript-starter", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vite-react-typescript-starter", + "version": "0.0.0", + "dependencies": { + "lucide-react": "^0.344.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.22.3" + }, + "devDependencies": { + "@eslint/js": "^9.9.1", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.18", + "eslint": "^9.9.1", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.11", + "globals": "^15.9.0", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.5.3", + "typescript-eslint": "^8.3.0", + "vite": "^5.4.2" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", + "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", + "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.7.tgz", + "integrity": "sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.7.tgz", + "integrity": "sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", + "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", + "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", + "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", + "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", + "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.0", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", + "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", + "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/type-utils": "8.8.1", + "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", + "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/typescript-estree": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", + "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", + "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.8.1", + "@typescript-eslint/utils": "8.8.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", + "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", + "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", + "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/typescript-estree": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", + "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.2.tgz", + "integrity": "sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001667", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", + "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.33", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz", + "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", + "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.6.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.12.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.1.0-rc-fb9a90fa48-20240614", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0-rc-fb9a90fa48-20240614.tgz", + "integrity": "sha512-xsiRwaDNF5wWNC4ZHLut+x/YcAxksUd9Rizt7LaEn3bV8VyYRpXnRJQlLOfYaVy9esk4DFP4zPPnoNVjq5Gc0w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.12.tgz", + "integrity": "sha512-9neVjoGv20FwYtCP6CB1dzR1vr57ZDNOXst21wd2xJ/cTlM2xLq0GWVlSNTdMn/4BtP6cHYBMCSp1wFBJ9jBsg==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "dev": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", + "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "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==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.344.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.344.0.tgz", + "integrity": "sha512-6YyBnn91GB45VuVT96bYCOKElbJzUHqp65vX8cDcu55MQL9T969v4dhGClpljamuI/+KMO9P6w9Acq1CVQGvIQ==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz", + "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz", + "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "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==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", + "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.1.tgz", + "integrity": "sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.8.1", + "@typescript-eslint/parser": "8.8.1", + "@typescript-eslint/utils": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vite": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 000000000..be28da611 --- /dev/null +++ b/web/package.json @@ -0,0 +1,34 @@ +{ + "name": "vite-react-typescript-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "lucide-react": "^0.344.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.22.3" + }, + "devDependencies": { + "@eslint/js": "^9.9.1", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.18", + "eslint": "^9.9.1", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.11", + "globals": "^15.9.0", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.5.3", + "typescript-eslint": "^8.3.0", + "vite": "^5.4.2" + } +} \ No newline at end of file diff --git a/web/postcss.config.js b/web/postcss.config.js new file mode 100644 index 000000000..2aa7205d4 --- /dev/null +++ b/web/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/web/src/App.tsx b/web/src/App.tsx new file mode 100644 index 000000000..1b0f9fdd5 --- /dev/null +++ b/web/src/App.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { ConfigProvider } from './contexts/ConfigContext'; +import { WizardModeProvider } from './contexts/WizardModeContext'; +import InstallWizard from './components/wizard/InstallWizard'; +import PrototypeSettings from './components/prototype/PrototypeSettings'; + +function App() { + return ( + +
+ + + } /> + + + + } + /> + + + + } + /> + } /> + + +
+
+ ); +} + +export default App; \ No newline at end of file diff --git a/web/src/components/common/Button.tsx b/web/src/components/common/Button.tsx new file mode 100644 index 000000000..3f2d9d2f1 --- /dev/null +++ b/web/src/components/common/Button.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { useConfig } from '../../contexts/ConfigContext'; + +interface ButtonProps { + children: React.ReactNode; + onClick?: () => void; + type?: 'button' | 'submit' | 'reset'; + variant?: 'primary' | 'secondary' | 'outline' | 'danger'; + size?: 'sm' | 'md' | 'lg'; + disabled?: boolean; + className?: string; + icon?: React.ReactNode; +} + +const Button: React.FC = ({ + children, + onClick, + type = 'button', + variant = 'primary', + size = 'md', + disabled = false, + className = '', + icon, +}) => { + const { prototypeSettings } = useConfig(); + const themeColor = prototypeSettings.themeColor; + + const baseStyles = 'inline-flex items-center justify-center font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-md'; + + const variantStyles = { + primary: `bg-[${themeColor}] hover:bg-[${themeColor}] text-white focus:ring-[${themeColor}]`, + secondary: 'bg-[#3498DB] hover:bg-[#2980B9] text-white focus:ring-[#3498DB]', + outline: `border border-gray-300 bg-white hover:bg-gray-50 text-gray-700 focus:ring-[${themeColor}]`, + danger: 'bg-red-500 hover:bg-red-600 text-white focus:ring-red-500', + }; + + const sizeStyles = { + sm: 'px-3 py-1.5 text-sm', + md: 'px-4 py-2 text-sm', + lg: 'px-5 py-2.5 text-base', + }; + + const disabledStyles = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'; + + return ( + + ); +}; + +export default Button; \ No newline at end of file diff --git a/web/src/components/common/Card.tsx b/web/src/components/common/Card.tsx new file mode 100644 index 000000000..d073f3e56 --- /dev/null +++ b/web/src/components/common/Card.tsx @@ -0,0 +1,39 @@ +import React from 'react'; + +interface CardProps { + children: React.ReactNode; + title?: string; + className?: string; + titleClassName?: string; + contentClassName?: string; + footer?: React.ReactNode; + footerClassName?: string; +} + +const Card: React.FC = ({ + children, + title, + className = '', + titleClassName = '', + contentClassName = '', + footer, + footerClassName = '', +}) => { + return ( +
+ {title && ( +
+

{title}

+
+ )} +
{children}
+ {footer && ( +
+ {footer} +
+ )} +
+ ); +}; + +export default Card; \ No newline at end of file diff --git a/web/src/components/common/Input.tsx b/web/src/components/common/Input.tsx new file mode 100644 index 000000000..56b40482d --- /dev/null +++ b/web/src/components/common/Input.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { useConfig } from '../../contexts/ConfigContext'; + +interface InputProps { + id: string; + label: string; + type?: string; + value: string; + onChange: (e: React.ChangeEvent) => void; + onKeyDown?: (e: React.KeyboardEvent) => void; + placeholder?: string; + required?: boolean; + disabled?: boolean; + error?: string; + helpText?: string; + className?: string; + labelClassName?: string; + icon?: React.ReactNode; +} + +const Input: React.FC = ({ + id, + label, + type = 'text', + value, + onChange, + onKeyDown, + placeholder = '', + required = false, + disabled = false, + error, + helpText, + className = '', + labelClassName = '', + icon, +}) => { + const { prototypeSettings } = useConfig(); + const themeColor = prototypeSettings.themeColor; + + return ( +
+ +
+ {icon && ( +
+ {icon} +
+ )} + +
+ {error &&

{error}

} + {helpText && !error &&

{helpText}

} +
+ ); +}; + +export default Input; \ No newline at end of file diff --git a/web/src/components/common/Logo.tsx b/web/src/components/common/Logo.tsx new file mode 100644 index 000000000..28517b3b9 --- /dev/null +++ b/web/src/components/common/Logo.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export const GiteaLogo: React.FC<{ className?: string }> = ({ className = 'w-6 h-6' }) => { + return ( + Gitea Logo + ); +}; \ No newline at end of file diff --git a/web/src/components/common/Select.tsx b/web/src/components/common/Select.tsx new file mode 100644 index 000000000..e2a7df559 --- /dev/null +++ b/web/src/components/common/Select.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { useConfig } from '../../contexts/ConfigContext'; + +interface SelectOption { + value: string; + label: string; +} + +interface SelectProps { + id: string; + label: string; + value: string; + onChange: (e: React.ChangeEvent) => void; + options: SelectOption[]; + required?: boolean; + disabled?: boolean; + error?: string; + helpText?: string; + className?: string; + labelClassName?: string; +} + +const Select: React.FC = ({ + id, + label, + value, + onChange, + options, + required = false, + disabled = false, + error, + helpText, + className = '', + labelClassName = '', +}) => { + const { prototypeSettings } = useConfig(); + const themeColor = prototypeSettings.themeColor; + + return ( +
+ + + {error &&

{error}

} + {helpText && !error &&

{helpText}

} +
+ ); +}; + +export default Select; \ No newline at end of file diff --git a/web/src/components/prototype/PrototypeSettings.tsx b/web/src/components/prototype/PrototypeSettings.tsx new file mode 100644 index 000000000..5bba97551 --- /dev/null +++ b/web/src/components/prototype/PrototypeSettings.tsx @@ -0,0 +1,197 @@ +import React from 'react'; +import Card from '../common/Card'; +import Select from '../common/Select'; +import { GiteaLogo } from '../common/Logo'; +import { useConfig } from '../../contexts/ConfigContext'; + +const PrototypeSettings: React.FC = () => { + const { prototypeSettings, updatePrototypeSettings } = useConfig(); + + const handleSkipValidationChange = (e: React.ChangeEvent) => { + updatePrototypeSettings({ skipValidation: e.target.checked }); + }; + + const handleFailPreflightsChange = (e: React.ChangeEvent) => { + updatePrototypeSettings({ failPreflights: e.target.checked }); + }; + + const handleFailHostPreflightsChange = (e: React.ChangeEvent) => { + updatePrototypeSettings({ failHostPreflights: e.target.checked }); + }; + + const handleFailInstallationChange = (e: React.ChangeEvent) => { + updatePrototypeSettings({ failInstallation: e.target.checked }); + }; + + const handleThemeColorChange = (e: React.ChangeEvent) => { + updatePrototypeSettings({ themeColor: e.target.value }); + }; + + const handleSelfSignedCertChange = (e: React.ChangeEvent) => { + updatePrototypeSettings({ useSelfSignedCert: e.target.checked }); + }; + + const handleSkipNodeValidationChange = (e: React.ChangeEvent) => { + updatePrototypeSettings({ skipNodeValidation: e.target.checked }); + }; + + const handleMultiNodeChange = (e: React.ChangeEvent) => { + updatePrototypeSettings({ enableMultiNode: e.target.checked }); + }; + + return ( +
+
+
+
+
+ +
+

Prototype Settings

+

Configure prototype behavior

+
+
+
+
+
+ +
+ +
+
+

Theme Settings

+
+ + +

Enter a hex color code (e.g., #609926)

+
+
+ +
+

Security Settings

+
+
+ + +
+
+
+ +
+

Linux Installation Settings

+
+
+ + +
+
+
+ +
+

Validation Settings

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +

+ These settings affect how the installer behaves during development and testing. +

+
+
+
+
+
+ ); +}; + +export default PrototypeSettings; \ No newline at end of file diff --git a/web/src/components/wizard/CompletionStep.tsx b/web/src/components/wizard/CompletionStep.tsx new file mode 100644 index 000000000..3343452e6 --- /dev/null +++ b/web/src/components/wizard/CompletionStep.tsx @@ -0,0 +1,130 @@ +import React, { useState } from 'react'; +import Card from '../common/Card'; +import Button from '../common/Button'; +import { useConfig } from '../../contexts/ConfigContext'; +import { CheckCircle, ExternalLink, Copy, ClipboardCheck } from 'lucide-react'; + +const CompletionStep: React.FC = () => { + const { config, prototypeSettings } = useConfig(); + const [copied, setCopied] = useState(false); + const themeColor = prototypeSettings.themeColor; + + const baseUrl = `${config.useHttps ? 'https' : 'http'}://${config.domain}`; + const urls = [ + { name: 'Web Interface', url: baseUrl, description: 'Access the main Gitea interface' }, + { name: 'API Documentation', url: `${baseUrl}/api/swagger`, description: 'Browse and test the Gitea API' } + ]; + + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }); + }; + + return ( +
+ +
+
+ +
+

Installation Complete!

+

+ Gitea Enterprise is installed successfully. +

+ + + +
+ {urls.map((item, index) => ( +
+
+

{item.name}

+ +
+ + {item.url} + + +

{item.description}

+
+ ))} +
+
+
+ + +
+

Next Steps

+ +
+
+
+
+ 1 +
+
+
+

Log in to your Gitea Enterprise instance

+

+ Use the administrator credentials you provided during setup to log in to your Gitea Enterprise instance. +

+
+
+ +
+
+
+ 2 +
+
+
+

Configure additional settings

+

+ Visit the Admin Dashboard to configure additional settings such as authentication providers, + webhooks, and other integrations. +

+
+
+ +
+
+
+ 3 +
+
+
+

Create your first organization

+

+ Set up an organization for your team and invite members to collaborate on repositories. +

+
+
+
+
+
+
+ ); +}; + +export default CompletionStep; \ No newline at end of file diff --git a/web/src/components/wizard/ConfigurationStep.tsx b/web/src/components/wizard/ConfigurationStep.tsx new file mode 100644 index 000000000..31fafe098 --- /dev/null +++ b/web/src/components/wizard/ConfigurationStep.tsx @@ -0,0 +1,476 @@ +import React, { useState, useRef } from 'react'; +import Card from '../common/Card'; +import Button from '../common/Button'; +import Input from '../common/Input'; +import Select from '../common/Select'; +import { useConfig } from '../../contexts/ConfigContext'; +import { useWizardMode } from '../../contexts/WizardModeContext'; +import { ChevronLeft, ChevronRight, Upload } from 'lucide-react'; + +interface ConfigurationStepProps { + onNext: () => void; + onBack: () => void; +} + +interface ValidationErrors { + clusterName?: string; + namespace?: string; + storageClass?: string; + domain?: string; + adminUsername?: string; + adminPassword?: string; + adminEmail?: string; + description?: string; + [key: string]: string | undefined; +} + +type TabName = 'cluster' | 'network' | 'admin' | 'database'; + +const ConfigurationStep: React.FC = ({ onNext, onBack }) => { + const { config, updateConfig, prototypeSettings } = useConfig(); + const { text } = useWizardMode(); + const [activeTab, setActiveTab] = useState('cluster'); + const [errors, setErrors] = useState({}); + const fileInputRef = useRef(null); + const themeColor = prototypeSettings.themeColor; + + const handleInputChange = (e: React.ChangeEvent) => { + const { id, value } = e.target; + updateConfig({ [id]: value }); + if (!prototypeSettings.skipValidation) { + setErrors(prev => ({ ...prev, [id]: undefined })); + } + }; + + const handleSelectChange = (e: React.ChangeEvent) => { + const { id, value } = e.target; + updateConfig({ [id]: value }); + }; + + const handleCheckboxChange = (e: React.ChangeEvent) => { + const { id, checked } = e.target; + updateConfig({ [id]: checked }); + }; + + const handleRadioChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + updateConfig({ [name]: value }); + }; + + const handleFileChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (event) => { + const content = event.target?.result; + updateConfig({ licenseKey: content as string }); + }; + reader.readAsText(file); + } + }; + + const validateTab = (tab: TabName): boolean => { + if (prototypeSettings.skipValidation) return true; + + const newErrors: ValidationErrors = {}; + + switch (tab) { + case 'cluster': + if (!config.clusterName) newErrors.clusterName = 'Cluster name is required'; + if (!config.namespace) newErrors.namespace = 'Namespace is required'; + if (!config.storageClass) newErrors.storageClass = 'Storage class is required'; + if (!config.description) newErrors.description = 'Description is required'; + break; + case 'network': + if (!config.domain) newErrors.domain = 'Domain is required'; + break; + case 'admin': + if (!config.adminUsername) newErrors.adminUsername = 'Admin username is required'; + if (!config.adminPassword) newErrors.adminPassword = 'Admin password is required'; + if (!config.adminEmail) newErrors.adminEmail = 'Admin email is required'; + break; + case 'database': + if (config.databaseType === 'external') { + if (!config.databaseConfig?.host) newErrors['databaseConfig.host'] = 'Database host is required'; + if (!config.databaseConfig?.username) newErrors['databaseConfig.username'] = 'Database username is required'; + if (!config.databaseConfig?.password) newErrors['databaseConfig.password'] = 'Database password is required'; + if (!config.databaseConfig?.database) newErrors['databaseConfig.database'] = 'Database name is required'; + } + break; + } + + setErrors(prev => ({ ...prev, ...newErrors })); + return Object.keys(newErrors).length === 0; + }; + + const findFirstTabWithErrors = (): TabName | null => { + const tabs: TabName[] = ['cluster', 'network', 'admin', 'database']; + for (const tab of tabs) { + if (!validateTab(tab)) { + return tab; + } + } + return null; + }; + + const handleNext = () => { + if (validateTab(activeTab)) { + const nextTabWithErrors = findFirstTabWithErrors(); + if (nextTabWithErrors) { + setActiveTab(nextTabWithErrors); + } else { + onNext(); + } + } + }; + + const renderClusterConfig = () => ( +
+ + + + +
+
+ + +
+ +

Choose your deployment configuration

+ + +
+ +